From d1ac50019ddb44e2b79154dd2167a71e7eea657a Mon Sep 17 00:00:00 2001 From: artemp Date: Fri, 10 May 2013 16:50:28 +0100 Subject: [PATCH 001/110] + fix writing to std::stringstream * if seeking pass the end - grow buffer (stringstream!) * seekp beyond current buffer size doesn't set failbit (clang/libcxx) --- include/mapnik/tiff_io.hpp | 95 ++++++++++++++++++++++++++++++-------- 1 file changed, 75 insertions(+), 20 deletions(-) diff --git a/include/mapnik/tiff_io.hpp b/include/mapnik/tiff_io.hpp index 8d6dc8833..18b666180 100644 --- a/include/mapnik/tiff_io.hpp +++ b/include/mapnik/tiff_io.hpp @@ -24,8 +24,7 @@ #define MAPNIK_TIFF_IO_HPP #include - -#include +#include extern "C" { @@ -45,37 +44,89 @@ namespace mapnik { static tsize_t tiff_write_proc(thandle_t fd, tdata_t buf, tsize_t size) { - std::ostream* out = (std::ostream*)fd; + std::ostream* out = reinterpret_cast(fd); + std::ios::pos_type pos = out->tellp(); + std::streamsize request_size = size; + if (static_cast(request_size) != size) + return static_cast(-1); + out->write(reinterpret_cast(buf), size); - out->write((const char*)buf, size); - - return size; + if( static_cast(pos) == -1 ) + { + return size; + } + else + { + return static_cast(out->tellp()-pos); + } } static toff_t tiff_seek_proc(thandle_t fd, toff_t off, int whence) { - if (off == 0xFFFFFFFF) - { - return 0xFFFFFFFF; - } + std::ostream* out = reinterpret_cast(fd); - std::ostream* out = (std::ostream*)fd; + if( out->fail() ) + return static_cast(-1); + + if( static_cast(out->tellp()) == -1) + return static_cast< toff_t >( 0 ); switch(whence) { + case SEEK_SET: + out->seekp(off, std::ios_base::beg); + break; case SEEK_CUR: out->seekp(off, std::ios_base::cur); break; case SEEK_END: out->seekp(off, std::ios_base::end); break; - case SEEK_SET: - default: - out->seekp(off, std::ios_base::beg); - break; } + // grow std::stringstream buffer (re: libtiff/tif_stream.cxx) + std::ios::pos_type pos = out->tellp(); + // second check needed for clang (libcxx doesn't set failbit when seeking beyond the current buffer size + if( out->fail() || off != pos) + { + std::ios::iostate old_state; + std::ios::pos_type origin; + old_state = out->rdstate(); + // reset the fail bit or else tellp() won't work below + out->clear(out->rdstate() & ~std::ios::failbit); + switch( whence ) + { + case SEEK_SET: + default: + origin = 0L; + break; + case SEEK_CUR: + origin = out->tellp(); + break; + case SEEK_END: + out->seekp(0, std::ios::end); + origin = out->tellp(); + break; + } + // restore original stream state + out->clear(old_state); - return (toff_t)out->tellp(); + // only do something if desired seek position is valid + if( (static_cast(origin) + off) > 0L) + { + uint64_t num_fill; + // clear the fail bit + out->clear(out->rdstate() & ~std::ios::failbit); + // extend the stream to the expected size + out->seekp(0, std::ios::end); + num_fill = (static_cast(origin)) + off - out->tellp(); + for( uint64_t i = 0; i < num_fill; ++i) + out->put('\0'); + + // retry the seek + out->seekp(static_cast(static_cast(origin) + off), std::ios::beg); + } + } + return static_cast(out->tellp()); } static int tiff_close_proc(thandle_t fd) @@ -87,8 +138,12 @@ static int tiff_close_proc(thandle_t fd) static toff_t tiff_size_proc(thandle_t fd) { - std::ostream* out = (std::ostream*)fd; - return (toff_t)out->tellp(); + std::ostream* out = reinterpret_cast(fd); + std::ios::pos_type pos = out->tellp(); + out->seekp(0, std::ios::end); + std::ios::pos_type len = out->tellp(); + out->seekp(pos); + return (toff_t)len; } static tsize_t tiff_dummy_read_proc(thandle_t fd, tdata_t buf, tsize_t size) @@ -113,7 +168,7 @@ void save_as_tiff(T1 & file, T2 const& image) const int scanline_size = sizeof(unsigned char) * width * 3; TIFF* output = RealTIFFOpen("mapnik_tiff_stream", - "w", + "wm", (thandle_t)&file, tiff_dummy_read_proc, tiff_write_proc, @@ -124,7 +179,7 @@ void save_as_tiff(T1 & file, T2 const& image) tiff_dummy_unmap_proc); if (! output) { - // throw ? + throw ImageWriterException("Could not write TIFF"); } TIFFSetField(output, TIFFTAG_IMAGEWIDTH, width); From c127757cd606ce48e94aaaea538c22b0b13db686 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 16 May 2013 07:40:54 -0700 Subject: [PATCH 002/110] remove uneeded exception translator for mapnik::config_error - works around #1847 --- bindings/python/mapnik_python.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/bindings/python/mapnik_python.cpp b/bindings/python/mapnik_python.cpp index 4ef560c23..38ae41305 100644 --- a/bindings/python/mapnik_python.cpp +++ b/bindings/python/mapnik_python.cpp @@ -84,7 +84,6 @@ void export_logger(); #include #include #include -#include #include #include #include @@ -333,11 +332,6 @@ double scale_denominator(mapnik::Map const &map, bool geographic) } // http://docs.python.org/c-api/exceptions.html#standard-exceptions -void config_error_translator(mapnik::config_error const & ex) -{ - PyErr_SetString(PyExc_RuntimeError, ex.what()); -} - void value_error_translator(mapnik::value_error const & ex) { PyErr_SetString(PyExc_ValueError, ex.what()); @@ -433,7 +427,6 @@ BOOST_PYTHON_MODULE(_mapnik) register_exception_translator(&standard_error_translator); register_exception_translator(&out_of_range_error_translator); - register_exception_translator(&config_error_translator); register_exception_translator(&value_error_translator); register_exception_translator(&runtime_error_translator); register_cairo(); From 4775428dc052dd9166d2e39bcbeef5011a840467 Mon Sep 17 00:00:00 2001 From: artemp Date: Thu, 16 May 2013 16:24:18 +0100 Subject: [PATCH 003/110] + move to QT 5.x (requirement!) --- demo/viewer/main.cpp | 2 +- demo/viewer/mainwindow.cpp | 5 ++++- demo/viewer/mainwindow.hpp | 2 -- demo/viewer/mapwidget.cpp | 38 +------------------------------------- demo/viewer/viewer.pro | 2 ++ 5 files changed, 8 insertions(+), 41 deletions(-) diff --git a/demo/viewer/main.cpp b/demo/viewer/main.cpp index 73af0c95c..6d83c46a9 100644 --- a/demo/viewer/main.cpp +++ b/demo/viewer/main.cpp @@ -19,7 +19,7 @@ // qt -#include +#include #include #include #include diff --git a/demo/viewer/mainwindow.cpp b/demo/viewer/mainwindow.cpp index 996138678..e87e7a0d4 100644 --- a/demo/viewer/mainwindow.cpp +++ b/demo/viewer/mainwindow.cpp @@ -32,7 +32,10 @@ #include #include #include - +#include +#include +#include +#include // mapnik #ifndef Q_MOC_RUN // QT moc chokes on BOOST_JOIN diff --git a/demo/viewer/mainwindow.hpp b/demo/viewer/mainwindow.hpp index 99936da73..f5f01863f 100644 --- a/demo/viewer/mainwindow.hpp +++ b/demo/viewer/mainwindow.hpp @@ -22,7 +22,6 @@ #define MAINWINDOW_HPP #include -#include #include #include #include @@ -78,7 +77,6 @@ private: LayerTab *layerTab_; StyleTab * styleTab_; MapWidget * mapWidget_; - QPrinter printer; //actions QList exportAsActs; QActionGroup *toolsGroup; diff --git a/demo/viewer/mapwidget.cpp b/demo/viewer/mapwidget.cpp index 0b8753e44..93be08351 100644 --- a/demo/viewer/mapwidget.cpp +++ b/demo/viewer/mapwidget.cpp @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -528,42 +527,7 @@ void render_agg(mapnik::Map const& map, double scaling_factor, QPixmap & pix) void render_grid(mapnik::Map const& map, double scaling_factor, QPixmap & pix) { - unsigned width=map.width(); - unsigned height=map.height(); - - mapnik::grid buf(width,height,"F_CODE", 1); - mapnik::grid_renderer ren(map,buf,scaling_factor); - - try - { - ren.apply(); - mapnik::value_integer * imdata = static_cast(buf.raw_data()); - - // Not sure how to display long long values ?? - //QImage image(width,height,QImage::Format_RGB32); - //for (unsigned i = 0 ; i < height ; ++i) - //{ - // for (unsigned j = 0 ; j < width ; ++j) - // { - // image.setPixel(j,i,qRgb((uint8_t)(imdata[i*width+j]>>8), - // (uint8_t)(imdata[i*width+j+1]>>8), - // (uint8_t)(imdata[i*width+j+2]>>8))); - // } - //} - //pix = QPixmap::fromImage(image); - } - catch (mapnik::config_error & ex) - { - std::cerr << ex.what() << std::endl; - } - catch (const std::exception & ex) - { - std::cerr << "exception: " << ex.what() << std::endl; - } - catch (...) - { - std::cerr << "Unknown exception caught!\n"; - } + std::cerr << "Not supported" << std::endl; } diff --git a/demo/viewer/viewer.pro b/demo/viewer/viewer.pro index adf2a46be..a19bc9c95 100644 --- a/demo/viewer/viewer.pro +++ b/demo/viewer/viewer.pro @@ -2,8 +2,10 @@ # Mapnik viewer - Copyright (C) 2007 Artem Pavlenko ###################################################################### TEMPLATE = app +QT += core gui widgets QMAKE_CXX = clang++ QMAKE_CXXFLAGS += $$system(mapnik-config --cxxflags) +QMAKE_CXXFLAGS += $$system(mapnik-config --includes --dep-includes) QMAKE_LFLAGS += $$system(mapnik-config --libs) QMAKE_LFLAGS += $$system(mapnik-config --ldflags --dep-libs) QMAKE_LFLAGS += -lboost_timer From 5ff69781c8f7d49fb3cf116f3a758869aa2cceba Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 16 May 2013 08:28:38 -0700 Subject: [PATCH 004/110] scons: disable cairo support at configure time unless cairo reports freetype support - closes #1842 --- SConstruct | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/SConstruct b/SConstruct index 5cba24449..6b66f168f 100644 --- a/SConstruct +++ b/SConstruct @@ -799,6 +799,28 @@ int main() context.Result(ret) return ret +def CheckCairoHasFreetype(context, silent=False): + if not silent: + context.Message('Checking that Cairo was built with freetype font support ... ') + ret = context.TryRun(""" + +#include + +int main() +{ + #ifdef CAIRO_HAS_FT_FONT + return 0; + #else + return 1; + #endif +} + +""", '.cpp')[0] + if silent: + context.did_show_result=1 + context.Result(ret) + return ret + def GetBoostLibVersion(context): ret = context.TryRun(""" @@ -946,6 +968,7 @@ conf_tests = { 'prioritize_paths' : prioritize_paths, 'CheckPKGVersion' : CheckPKGVersion, 'FindBoost' : FindBoost, 'CheckBoost' : CheckBoost, + 'CheckCairoHasFreetype' : CheckCairoHasFreetype, 'GetBoostLibVersion' : GetBoostLibVersion, 'GetMapnikLibVersion' : GetMapnikLibVersion, 'parse_config' : parse_config, @@ -1411,6 +1434,11 @@ if not preconfigured: else: color_print(4,'Not building with cairo support, pass CAIRO=True to enable') + if not env['HOST'] and env['HAS_CAIRO']: + if not conf.CheckCairoHasFreetype(): + env['SKIPPED_DEPS'].append('cairo') + env['HAS_CAIRO'] = False + if 'python' in env['BINDINGS'] or 'python' in env['REQUESTED_PLUGINS']: if not os.access(env['PYTHON'], os.X_OK): color_print(1,"Cannot run python interpreter at '%s', make sure that you have the permissions to execute it." % env['PYTHON']) From 04d119fc92423063ef14bf34718d3874d1d2b1a7 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 16 May 2013 09:59:21 -0700 Subject: [PATCH 005/110] further improve cairo freetype support checking - refs #1842 --- SConstruct | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/SConstruct b/SConstruct index 6b66f168f..90baf19fc 100644 --- a/SConstruct +++ b/SConstruct @@ -22,6 +22,7 @@ import sys import re import platform from glob import glob +from copy import copy from subprocess import Popen, PIPE from SCons.SConf import SetCacheMode import pickle @@ -171,6 +172,10 @@ def shortest_name(libs): name = lib return name +def rm_path(item,set,_env): + for i in _env[set]: + if item in i: + _env[set].remove(i) def sort_paths(items,priority): """Sort paths such that compiling and linking will globally prefer custom or local libs @@ -802,9 +807,11 @@ int main() def CheckCairoHasFreetype(context, silent=False): if not silent: context.Message('Checking that Cairo was built with freetype font support ... ') + context.env.AppendUnique(CPPPATH=copy(env['CAIRO_CPPPATHS'])) + ret = context.TryRun(""" -#include +#include int main() { @@ -819,6 +826,9 @@ int main() if silent: context.did_show_result=1 context.Result(ret) + for item in env['CAIRO_CPPPATHS']: + print 'removing %s' % item + rm_path(item,'CPPPATH',context.env) return ret def GetBoostLibVersion(context): @@ -1724,15 +1734,11 @@ if not HELP_REQUESTED: p = env['PATH_REMOVE'] if p in env['ENV']['PATH']: env['ENV']['PATH'].replace(p,'') - def rm_path(set): - for i in env[set]: - if p in i: - env[set].remove(i) - rm_path('LIBPATH') - rm_path('CPPPATH') - rm_path('CXXFLAGS') - rm_path('CAIRO_LIBPATHS') - rm_path('CAIRO_CPPPATHS') + rm_path(p,'LIBPATH',env) + rm_path(p,'CPPPATH',env) + rm_path(p,'CXXFLAGS',env) + rm_path(p,'CAIRO_LIBPATHS',env) + rm_path(p,'CAIRO_CPPPATHS',env) if env['PATH_REPLACE']: searches,replace = env['PATH_REPLACE'].split(':') From c53bf9fff1a1e5ff52a024a8e12c01b27e0a627c Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 16 May 2013 10:03:08 -0700 Subject: [PATCH 006/110] remove debug output --- SConstruct | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/SConstruct b/SConstruct index 90baf19fc..e6e3ac62f 100644 --- a/SConstruct +++ b/SConstruct @@ -806,7 +806,7 @@ int main() def CheckCairoHasFreetype(context, silent=False): if not silent: - context.Message('Checking that Cairo was built with freetype font support ... ') + context.Message('Checking for cairo freetype font support ... ') context.env.AppendUnique(CPPPATH=copy(env['CAIRO_CPPPATHS'])) ret = context.TryRun(""" @@ -827,7 +827,6 @@ int main() context.did_show_result=1 context.Result(ret) for item in env['CAIRO_CPPPATHS']: - print 'removing %s' % item rm_path(item,'CPPPATH',context.env) return ret From 3436c324e8c4539c8c1e9b93b74beae569e2db60 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 16 May 2013 10:16:51 -0700 Subject: [PATCH 007/110] only run cairo visual rendering tests if cairo backend is available --- tests/visual_tests/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/visual_tests/test.py b/tests/visual_tests/test.py index 076f2d1ae..d149b8cfc 100755 --- a/tests/visual_tests/test.py +++ b/tests/visual_tests/test.py @@ -19,7 +19,7 @@ defaults = { 'sizes': [(500, 100)], 'scales':[1.0,2.0], 'agg': True, - 'cairo': True, + 'cairo': mapnik.has_cairo(), 'grid': True, } From 1c03b9fefac129a4770c1b9ab266b78ae46e5c32 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 16 May 2013 10:17:26 -0700 Subject: [PATCH 008/110] update expected grid test --- ...rasterizer-600-400-1.0-grid-reference.json | 80 ++++++++++--------- 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/tests/visual_tests/grids/text-halo-rasterizer-600-400-1.0-grid-reference.json b/tests/visual_tests/grids/text-halo-rasterizer-600-400-1.0-grid-reference.json index ee24381b3..56a29ed9e 100644 --- a/tests/visual_tests/grids/text-halo-rasterizer-600-400-1.0-grid-reference.json +++ b/tests/visual_tests/grids/text-halo-rasterizer-600-400-1.0-grid-reference.json @@ -1,16 +1,22 @@ { "keys": [ "", - "5", - "10", - "4", - "9", - "3", - "8", - "2", - "7", "1", - "6" + "9", + "2", + "10", + "3", + "11", + "4", + "12", + "5", + "13", + "6", + "14", + "7", + "15", + "8", + "16" ], "data": {}, "grid": [ @@ -22,20 +28,10 @@ " ", " ", " ", - " !!! !!!!!! ### ###### ", - " !!!!!!!!!!!! ########## ", - " !!!!!!!!!!!! ########### ", - " !!!!!!!!!!! ########### ", - " !!!!!!!!!!! ####### ## ", " ", - " ", - " ", - " ", - " ", - " $$ $$$$$$ %% %% %% %%% ", - " $$$$$$$$$$$$$$ %%%%%% %% %%% ", - " $$$$$$$$$$$$$$ %%%%%% %% %%% ", - " $$$$$$$$$$$$$ %%%%%%%%%%%%% ", + " ! ! ! ## ## ", + " ! ! !! ! #### # ", + " !!! ! ! !! #### ### ", " ", " ", " ", @@ -43,6 +39,9 @@ " ", " ", " ", + " $ $ $ %% %% ", + " $ $ $$ $$ %%%% % ", + " $$$ $ $ $$ %%%% %%% ", " ", " ", " ", @@ -50,12 +49,9 @@ " ", " ", " ", - " ", - " ", - " ", - " &&&&&&&&& && '''''' '' '' ", - " &&&&&& & && ' '''' ' ''' ", - " &&&&&&&&&&&&& ' '''' '''''' ", + " & & '' ' ", + " & & && & '''' '' ", + " &&& & & && '''' ''' ", " ", " ", " ", @@ -63,6 +59,9 @@ " ", " ", " ", + " ( ( (( )) )) ", + " ( ( (( )))) ) ", + " ((( ( ( ( )))) ) ", " ", " ", " ", @@ -70,12 +69,9 @@ " ", " ", " ", - " ", - " ", - " ", - " (((((((((( )) ))) ) ", - " (((((( ( )) ))) ) ", - " ((((((((( )))))) )) ", + " ********** ++ +++ + ", + " ****** * ++ +++ + ", + " ********* ++++++ ++ ", " ", " ", " ", @@ -83,21 +79,31 @@ " ", " ", " ", + " ,,,,,,,,, ,, ------ -- -- ", + " ,,,,,, , ,, - ---- - --- ", + " ,,,,,,,,,,,,, - ---- ------ ", " ", " ", " ", " ", " ", " ", + " .. ...... // // // /// ", + " .............. ////// // /// ", + " .............. ////// // /// ", + " ............. ///////////// ", " ", " ", " ", " ", - " * * ++ + ", - " * * ** * ++++ ++ ", - " *** * * ** ++++ +++ ", " ", " ", + " 000 000000 111 111111 ", + " 000000000000 1111111111 ", + " 000000000000 11111111111 ", + " 00000000000 11111111111 ", + " 00000000000 1111111 11 ", + " ", " ", " ", " ", From 8ed9606046271bf9eed674a0d2c1da0145fc1d7c Mon Sep 17 00:00:00 2001 From: artemp Date: Thu, 16 May 2013 19:14:10 +0100 Subject: [PATCH 009/110] * c++ style comments, pls! * formatting --- bindings/python/python_optional.hpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/bindings/python/python_optional.hpp b/bindings/python/python_optional.hpp index 0d785b0f3..dadd67264 100644 --- a/bindings/python/python_optional.hpp +++ b/bindings/python/python_optional.hpp @@ -100,11 +100,13 @@ struct python_optional : public boost::noncopyable } }; -/** This class works around a bug in boost python. +// This class works around a feature in boost python. +// See http://osdir.com/ml/python.c++/2003-11/msg00158.html - See http://osdir.com/ml/python.c++/2003-11/msg00158.html -*/ -template +template class class_with_converter : public boost::python::class_ { public: From a3900131fdb7e1f25d72895ddba3dcb59becbf48 Mon Sep 17 00:00:00 2001 From: artemp Date: Thu, 16 May 2013 19:36:50 +0100 Subject: [PATCH 010/110] + add python_optional specialization --- bindings/python/mapnik_markers_symbolizer.cpp | 4 +- bindings/python/python_optional.hpp | 51 ++++++++++++++++++- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/bindings/python/mapnik_markers_symbolizer.cpp b/bindings/python/mapnik_markers_symbolizer.cpp index 64f6bc394..cfe06efce 100644 --- a/bindings/python/mapnik_markers_symbolizer.cpp +++ b/bindings/python/mapnik_markers_symbolizer.cpp @@ -30,6 +30,8 @@ #include #include "mapnik_svg.hpp" #include "mapnik_enumeration.hpp" +#include "python_optional.hpp" + #include // for known_svg_prefix_ using mapnik::markers_symbolizer; @@ -119,7 +121,7 @@ void export_markers_symbolizer() &markers_symbolizer::set_opacity, "Set/get the overall opacity") .add_property("fill_opacity", - &get_fill_opacity_impl, + &markers_symbolizer::get_fill_opacity, &markers_symbolizer::set_fill_opacity, "Set/get the fill opacity") .add_property("ignore_placement", diff --git a/bindings/python/python_optional.hpp b/bindings/python/python_optional.hpp index dadd67264..4b5f3b531 100644 --- a/bindings/python/python_optional.hpp +++ b/bindings/python/python_optional.hpp @@ -74,7 +74,7 @@ struct python_optional : public boost::noncopyable rvalue_from_python_stage1(source, converters); return rvalue_from_python_stage2(source, data, converters); } - return NULL; + return 0; } static void construct(PyObject * source, @@ -94,12 +94,59 @@ struct python_optional : public boost::noncopyable } }; - explicit python_optional() { + explicit python_optional() + { register_python_conversion, optional_to_python, optional_from_python>(); } }; +// to/from optional +template <> +struct python_optional : public boost::noncopyable +{ + struct optional_to_python + { + static PyObject * convert(const boost::optional& value) + { + return (value ? PyFloat_FromDouble(*value) : + boost::python::detail::none()); + } + }; + + struct optional_from_python + { + static void * convertible(PyObject * source) + { + using namespace boost::python::converter; + + if (source == Py_None || PyFloat_Check(source)) + return source; + return 0; + } + + static void construct(PyObject * source, + boost::python::converter::rvalue_from_python_stage1_data * data) + { + using namespace boost::python::converter; + void * const storage = ((rvalue_from_python_storage > *) + data)->storage.bytes; + if (source == Py_None) // == None + new (storage) boost::optional(); // A Boost uninitialized value + else + new (storage) boost::optional(PyFloat_AsDouble(source)); + data->convertible = storage; + } + }; + + explicit python_optional() + { + register_python_conversion, + optional_to_python, optional_from_python>(); + } +}; + + // This class works around a feature in boost python. // See http://osdir.com/ml/python.c++/2003-11/msg00158.html From 37cc3de4aa3ec737895d210f69cd76fae1c394c0 Mon Sep 17 00:00:00 2001 From: artemp Date: Thu, 16 May 2013 19:41:41 +0100 Subject: [PATCH 011/110] + use mapnik::noncopyable --- bindings/python/python_optional.hpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/bindings/python/python_optional.hpp b/bindings/python/python_optional.hpp index 4b5f3b531..4af5db7e5 100644 --- a/bindings/python/python_optional.hpp +++ b/bindings/python/python_optional.hpp @@ -22,7 +22,6 @@ #include #include -#include // boost::optional to/from converter from John Wiegley @@ -46,7 +45,7 @@ struct register_python_conversion }; template -struct python_optional : public boost::noncopyable +struct python_optional : public mapnik::noncopyable { struct optional_to_python { @@ -101,9 +100,9 @@ struct python_optional : public boost::noncopyable } }; -// to/from optional +// to/from boost::optional template <> -struct python_optional : public boost::noncopyable +struct python_optional : public mapnik::noncopyable { struct optional_to_python { From c34f86e08fecc7b1fd62fad281285c5f1b4ca1e0 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 16 May 2013 11:29:45 -0700 Subject: [PATCH 012/110] finish cleanup after geos plugin removal - refs #1809 --- bindings/python/mapnik/__init__.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/bindings/python/mapnik/__init__.py b/bindings/python/mapnik/__init__.py index acb7dfe0b..4c4a1dd89 100644 --- a/bindings/python/mapnik/__init__.py +++ b/bindings/python/mapnik/__init__.py @@ -558,24 +558,6 @@ def Osm(**keywords): keywords['type'] = 'osm' return CreateDatasource(keywords) -def Geos(**keywords): - """Create a GEOS Vector Datasource. - - Required keyword arguments: - wkt -- inline WKT text of the geometry - - Optional keyword arguments: - extent -- manually specified data extent (comma delimited string, default None) - - >>> from mapnik import Geos, Layer - >>> datasource = Geos(wkt='MULTIPOINT(100 100, 50 50, 0 0)') - >>> lyr = Layer('GEOS Layer from WKT string') - >>> lyr.datasource = datasource - - """ - keywords['type'] = 'geos' - return CreateDatasource(keywords) - def Python(**keywords): """Create a Python Datasource. From 3fbf4df67d78dbf7f69bd05fe7d0bc199f73d632 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 16 May 2013 11:33:23 -0700 Subject: [PATCH 013/110] add support for statically linking datasource input plugins - closes #1810 and #1821 - refs #249 --- CHANGELOG.md | 2 + SConstruct | 34 +++--- bindings/python/build.py | 2 +- include/mapnik/datasource.hpp | 30 +++--- plugins/input/csv/build.py | 63 +++++++---- plugins/input/gdal/build.py | 52 +++++---- plugins/input/geojson/build.py | 50 ++++++--- plugins/input/occi/build.py | 46 +++++--- plugins/input/ogr/build.py | 67 +++++++----- plugins/input/osm/build.py | 47 ++++++--- plugins/input/postgis/build.py | 57 ++++++---- plugins/input/python/build.py | 110 ++++++++++++-------- plugins/input/raster/build.py | 48 ++++++--- plugins/input/rasterlite/build.py | 47 ++++++--- plugins/input/shape/build.py | 70 ++++++++----- plugins/input/sqlite/build.py | 51 +++++---- plugins/input/templates/helloworld/build.py | 87 +++++++++------- src/build.py | 43 ++++++-- src/datasource_cache.cpp | 70 ++++++++----- utils/mapnik-config/build.py | 1 + 20 files changed, 622 insertions(+), 355 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b54510ea5..327177a4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,6 +68,8 @@ For a complete change history, see the git log. now the combined layer extents will be again respected: they will be clipped to the maximum-extent if possible and only when back-projecting fails for all layers will the maximum-extent be used as a fallback (#1473) +- Compile time flag called `PLUGIN_LINKING` to allow input datasource plugins to be statically linked with the mapnik library (#249) + ## Mapnik 2.1.0 Released Aug 23, 2012 diff --git a/SConstruct b/SConstruct index e6e3ac62f..73a650874 100644 --- a/SConstruct +++ b/SConstruct @@ -313,8 +313,8 @@ opts.AddVariables( ('XML2_CONFIG', 'The path to the xml2-config executable.', 'xml2-config'), PathVariable('ICU_INCLUDES', 'Search path for ICU include files', '/usr/include', PathVariable.PathAccept), PathVariable('ICU_LIBS','Search path for ICU include files','/usr/' + LIBDIR_SCHEMA_DEFAULT, PathVariable.PathAccept), - ('ICU_LIB_NAME', 'The library name for icu (such as icuuc, sicuuc, or icucore)', 'icuuc', -PathVariable.PathAccept), + ('ICU_LIB_NAME', 'The library name for icu (such as icuuc, sicuuc, or icucore)', 'icuuc', PathVariable.PathAccept), + BoolVariable('PNG', 'Build Mapnik with PNG read and write support', 'True'), PathVariable('PNG_INCLUDES', 'Search path for libpng include files', '/usr/include', PathVariable.PathAccept), PathVariable('PNG_LIBS','Search path for libpng library files','/usr/' + LIBDIR_SCHEMA_DEFAULT, PathVariable.PathAccept), @@ -357,6 +357,9 @@ PathVariable.PathAccept), BoolVariable('ENABLE_STATS', 'Enable global statistics during map processing', 'False'), ('DEFAULT_LOG_SEVERITY', 'The default severity of the logger (eg. ' + ', '.join(severities) + ')', 'error'), + # Plugin linking + EnumVariable('PLUGIN_LINKING', "Set plugin linking with libmapnik", 'shared', ['shared','static']), + # Other variables BoolVariable('SHAPE_MEMORY_MAPPED_FILE', 'Utilize memory-mapped files in Shapefile Plugin (higher memory usage, better performance)', 'True'), ('SYSTEM_FONTS','Provide location for python bindings to register fonts (if provided then the bundled DejaVu fonts are not installed)',''), @@ -439,7 +442,7 @@ pickle_store = [# Scons internal variables 'LIBMAPNIK_DEFINES', 'LIBMAPNIK_CXXFLAGS', 'CAIRO_LIBPATHS', - 'CAIRO_LINKFLAGS', + 'CAIRO_ALL_LIBS', 'CAIRO_CPPPATHS', 'SVG_RENDERER', 'SQLITE_LINKFLAGS', @@ -1039,11 +1042,12 @@ if not preconfigured: env['SKIPPED_DEPS'] = [] env['HAS_CAIRO'] = False env['CAIRO_LIBPATHS'] = [] - env['CAIRO_LINKFLAGS'] = [] + env['CAIRO_ALL_LIBS'] = [] env['CAIRO_CPPPATHS'] = [] env['HAS_PYCAIRO'] = False env['HAS_LIBXML2'] = False env['LIBMAPNIK_LIBS'] = [] + env['LIBMAPNIK_LINKFLAGS'] = [] env['LIBMAPNIK_CPPATHS'] = [] env['LIBMAPNIK_DEFINES'] = [] env['LIBMAPNIK_CXXFLAGS'] = [] @@ -1401,9 +1405,9 @@ if not preconfigured: #os.path.join(c_inc,'include/libpng'), ] ) - env["CAIRO_LINKFLAGS"] = ['cairo'] + env["CAIRO_ALL_LIBS"] = ['cairo'] if env['RUNTIME_LINK'] == 'static': - env["CAIRO_LINKFLAGS"].extend( + env["CAIRO_ALL_LIBS"].extend( ['pixman-1','expat','fontconfig','iconv'] ) # todo - run actual checkLib? @@ -1426,7 +1430,7 @@ if not preconfigured: cairo_env.ParseConfig(cmd) for lib in cairo_env['LIBS']: if not lib in env['LIBS']: - env["CAIRO_LINKFLAGS"].append(lib) + env["CAIRO_ALL_LIBS"].append(lib) for lpath in cairo_env['LIBPATH']: if not lpath in env['LIBPATH']: env["CAIRO_LIBPATHS"].append(lpath) @@ -1794,7 +1798,8 @@ if not HELP_REQUESTED: for plugin in env['REQUESTED_PLUGINS']: details = env['PLUGINS'][plugin] if details['lib'] in env['LIBS']: - SConscript('plugins/input/%s/build.py' % plugin) + if env['PLUGIN_LINKING'] == 'shared': + SConscript('plugins/input/%s/build.py' % plugin) if plugin == 'ogr': OGR_BUILT = True if plugin == 'gdal': GDAL_BUILT = True if plugin == 'ogr' or plugin == 'gdal': @@ -1803,8 +1808,9 @@ if not HELP_REQUESTED: else: env['LIBS'].remove(details['lib']) elif not details['lib']: - # build internal shape and raster plugins - SConscript('plugins/input/%s/build.py' % plugin) + if env['PLUGIN_LINKING'] == 'shared': + # build internal datasource input plugins + SConscript('plugins/input/%s/build.py' % plugin) else: color_print(1,"Notice: dependencies not met for plugin '%s', not building..." % plugin) # also clear out locally built target @@ -1818,11 +1824,11 @@ if not HELP_REQUESTED: # installed plugins that we are no longer building if 'install' in COMMAND_LINE_TARGETS: for plugin in PLUGINS.keys(): - if plugin not in env['REQUESTED_PLUGINS']: - plugin_path = os.path.join(env['MAPNIK_INPUT_PLUGINS_DEST'],'%s.input' % plugin) - if os.path.exists(plugin_path): + plugin_path = os.path.join(env['MAPNIK_INPUT_PLUGINS_DEST'],'%s.input' % plugin) + if os.path.exists(plugin_path): + if plugin not in env['REQUESTED_PLUGINS'] or env['PLUGIN_LINKING'] == 'static': color_print(3,"Notice: removing out of date plugin: '%s'" % plugin_path) - os.unlink(plugin_path) + os.unlink(plugin_path) # Build the c++ rundemo app if requested if env['DEMO']: diff --git a/bindings/python/build.py b/bindings/python/build.py index 40bc6c254..44d8c1376 100644 --- a/bindings/python/build.py +++ b/bindings/python/build.py @@ -177,7 +177,7 @@ if 'uninstall' not in COMMAND_LINE_TARGETS: py_env.Append(CPPPATH = env['CAIRO_CPPPATHS']) py_env.Append(CPPDEFINES = '-DHAVE_CAIRO') if env['PLATFORM'] == 'Darwin': - py_env.Append(LIBS=env['CAIRO_LINKFLAGS']) + py_env.Append(LIBS=env['CAIRO_ALL_LIBS']) if env['HAS_PYCAIRO']: py_env.ParseConfig('pkg-config --cflags pycairo') diff --git a/include/mapnik/datasource.hpp b/include/mapnik/datasource.hpp index 2967cb5bb..b2edb91d4 100644 --- a/include/mapnik/datasource.hpp +++ b/include/mapnik/datasource.hpp @@ -134,19 +134,23 @@ public: typedef boost::shared_ptr datasource_ptr; -#define DATASOURCE_PLUGIN(classname) \ - extern "C" MAPNIK_EXP const char * datasource_name() \ - { \ - return classname::name(); \ - } \ - extern "C" MAPNIK_EXP datasource* create(parameters const& params) \ - { \ - return new classname(params); \ - } \ - extern "C" MAPNIK_EXP void destroy(datasource *ds) \ - { \ - delete ds; \ - } +#ifdef MAPNIK_STATIC_PLUGINS + #define DATASOURCE_PLUGIN(classname) +#else + #define DATASOURCE_PLUGIN(classname) \ + extern "C" MAPNIK_EXP const char * datasource_name() \ + { \ + return classname::name(); \ + } \ + extern "C" MAPNIK_EXP datasource* create(parameters const& params) \ + { \ + return new classname(params); \ + } \ + extern "C" MAPNIK_EXP void destroy(datasource *ds) \ + { \ + delete ds; \ + } +#endif } diff --git a/plugins/input/csv/build.py b/plugins/input/csv/build.py index ae41bf6d1..d3dfdea12 100644 --- a/plugins/input/csv/build.py +++ b/plugins/input/csv/build.py @@ -1,37 +1,62 @@ -#!/usr/bin/env python +# +# This file is part of Mapnik (c++ mapping toolkit) +# +# Copyright (C) 2013 Artem Pavlenko +# +# Mapnik is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +# -import os Import ('plugin_base') Import ('env') PLUGIN_NAME = 'csv' -install_dest = env['MAPNIK_INPUT_PLUGINS_DEST'] plugin_env = plugin_base.Clone() plugin_sources = Split( """ %(PLUGIN_NAME)s_datasource.cpp """ % locals() - ) +) +# Link Library to Dependencies libraries = [] -libraries.append('mapnik') libraries.append('boost_system%s' % env['BOOST_APPEND']) libraries.append(env['ICU_LIB_NAME']) - -TARGET = plugin_env.SharedLibrary( - '../%s' % PLUGIN_NAME, - SHLIBPREFIX='', - SHLIBSUFFIX='.input', - source=plugin_sources, - LIBS=libraries, - LINKFLAGS=env.get('CUSTOM_LDFLAGS') - ) -# if the plugin links to libmapnik ensure it is built first -Depends(TARGET, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) +if env['PLUGIN_LINKING'] == 'shared': + libraries.append('mapnik') -if 'uninstall' not in COMMAND_LINE_TARGETS: - env.Install(install_dest, TARGET) - env.Alias('install', install_dest) + TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME, + SHLIBPREFIX='', + SHLIBSUFFIX='.input', + source=plugin_sources, + LIBS=libraries, + LINKFLAGS=env.get('CUSTOM_LDFLAGS')) + + # if the plugin links to libmapnik ensure it is built first + Depends(TARGET, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) + + if 'uninstall' not in COMMAND_LINE_TARGETS: + env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], TARGET) + env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) + +plugin_obj = { + 'LIBS': libraries, + 'SOURCES': plugin_sources, +} + +Return('plugin_obj') diff --git a/plugins/input/gdal/build.py b/plugins/input/gdal/build.py index 501ff089f..2f2f535e2 100644 --- a/plugins/input/gdal/build.py +++ b/plugins/input/gdal/build.py @@ -1,7 +1,7 @@ # # This file is part of Mapnik (c++ mapping toolkit) # -# Copyright (C) 2007 Artem Pavlenko, Jean-Francois Doyon +# Copyright (C) 2013 Artem Pavlenko # # Mapnik is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -22,35 +22,47 @@ Import ('plugin_base') Import ('env') -prefix = env['PREFIX'] +PLUGIN_NAME = 'gdal' plugin_env = plugin_base.Clone() -gdal_src = Split( +plugin_sources = Split( """ - gdal_datasource.cpp - gdal_featureset.cpp - """ - ) - -# clear out and rebuild libs -plugin_env['LIBS'] = [env['PLUGINS']['gdal']['lib']] + %(PLUGIN_NAME)s_datasource.cpp + %(PLUGIN_NAME)s_featureset.cpp + """ % locals() +) # Link Library to Dependencies -plugin_env['LIBS'].append('mapnik') -plugin_env['LIBS'].append('boost_system%s' % env['BOOST_APPEND']) -plugin_env['LIBS'].append(env['ICU_LIB_NAME']) +libraries = [env['PLUGINS']['gdal']['lib']] +libraries.append('boost_system%s' % env['BOOST_APPEND']) +libraries.append(env['ICU_LIB_NAME']) if env['RUNTIME_LINK'] == 'static': cmd = 'gdal-config --dep-libs' plugin_env.ParseConfig(cmd) - plugin_env['LIBS'].append('proj') + libraries.append('proj') -input_plugin = plugin_env.SharedLibrary('../gdal', source=gdal_src, SHLIBPREFIX='', SHLIBSUFFIX='.input', LINKFLAGS=env['CUSTOM_LDFLAGS']) +if env['PLUGIN_LINKING'] == 'shared': + libraries.append('mapnik') -# if the plugin links to libmapnik ensure it is built first -Depends(input_plugin, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) + TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME, + SHLIBPREFIX='', + SHLIBSUFFIX='.input', + source=plugin_sources, + LIBS=libraries, + LINKFLAGS=env['CUSTOM_LDFLAGS']) -if 'uninstall' not in COMMAND_LINE_TARGETS: - env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], input_plugin) - env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) + # if the plugin links to libmapnik ensure it is built first + Depends(TARGET, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) + + if 'uninstall' not in COMMAND_LINE_TARGETS: + env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], TARGET) + env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) + +plugin_obj = { + 'LIBS': libraries, + 'SOURCES': plugin_sources, +} + +Return('plugin_obj') diff --git a/plugins/input/geojson/build.py b/plugins/input/geojson/build.py index f463862a1..73aeb125f 100644 --- a/plugins/input/geojson/build.py +++ b/plugins/input/geojson/build.py @@ -1,7 +1,7 @@ # # This file is part of Mapnik (c++ mapping toolkit) # -# Copyright (C) 2012 Artem Pavlenko +# Copyright (C) 2013 Artem Pavlenko # # Mapnik is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -17,7 +17,7 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # - +# Import ('env') @@ -32,27 +32,45 @@ if not can_build: print 'WARNING: skipping building the optional geojson datasource plugin which requires boost >= 1.47' else: Import ('plugin_base') - prefix = env['PREFIX'] + + PLUGIN_NAME = 'geojson' + plugin_env = plugin_base.Clone() - geojson_src = Split( + + plugin_sources = Split( """ - geojson_datasource.cpp - geojson_featureset.cpp - """ - ) - libraries = [] + %(PLUGIN_NAME)s_datasource.cpp + %(PLUGIN_NAME)s_featureset.cpp + """ % locals() + ) + # Link Library to Dependencies - libraries.append('mapnik') + libraries = [] libraries.append(env['ICU_LIB_NAME']) libraries.append('boost_system%s' % env['BOOST_APPEND']) if env['THREADING'] == 'multi': libraries.append('boost_thread%s' % env['BOOST_APPEND']) - input_plugin = plugin_env.SharedLibrary('../geojson', source=geojson_src, SHLIBPREFIX='', SHLIBSUFFIX='.input', LIBS=libraries, LINKFLAGS=env['CUSTOM_LDFLAGS']) + if env['PLUGIN_LINKING'] == 'shared': + libraries.append('mapnik') - # if the plugin links to libmapnik ensure it is built first - Depends(input_plugin, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) + TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME, + SHLIBPREFIX='', + SHLIBSUFFIX='.input', + source=plugin_sources, + LIBS=libraries, + LINKFLAGS=env['CUSTOM_LDFLAGS']) - if 'uninstall' not in COMMAND_LINE_TARGETS: - env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], input_plugin) - env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) + # if the plugin links to libmapnik ensure it is built first + Depends(TARGET, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) + + if 'uninstall' not in COMMAND_LINE_TARGETS: + env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], TARGET) + env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) + + plugin_obj = { + 'LIBS': libraries, + 'SOURCES': plugin_sources, + } + + Return('plugin_obj') diff --git a/plugins/input/occi/build.py b/plugins/input/occi/build.py index 050cbce9b..946d32dbb 100644 --- a/plugins/input/occi/build.py +++ b/plugins/input/occi/build.py @@ -1,7 +1,7 @@ # # This file is part of Mapnik (c++ mapping toolkit) # -# Copyright (C) 2007 Artem Pavlenko, Jean-Francois Doyon +# Copyright (C) 2013 Artem Pavlenko # # Mapnik is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -17,35 +17,49 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # -# +# Import ('plugin_base') Import ('env') -prefix = env['PREFIX'] +PLUGIN_NAME = 'occi' plugin_env = plugin_base.Clone() -occi_src = Split( +plugin_sources = Split( """ - occi_types.cpp - occi_datasource.cpp - occi_featureset.cpp + %(PLUGIN_NAME)s_types.cpp + %(PLUGIN_NAME)s_datasource.cpp + %(PLUGIN_NAME)s_featureset.cpp spatial_classesm.cpp spatial_classeso.cpp - """ - ) + """ % locals() +) libraries = [ 'occi', 'ociei' ] -libraries.append('mapnik') libraries.append('boost_system%s' % env['BOOST_APPEND']) libraries.append(env['ICU_LIB_NAME']) -input_plugin = plugin_env.SharedLibrary('../occi', source=occi_src, SHLIBPREFIX='', SHLIBSUFFIX='.input', LIBS=libraries, LINKFLAGS=env['CUSTOM_LDFLAGS']) +if env['PLUGIN_LINKING'] == 'shared': + libraries.append('mapnik') -# if the plugin links to libmapnik ensure it is built first -Depends(input_plugin, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) + TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME, + SHLIBPREFIX='', + SHLIBSUFFIX='.input', + source=plugin_sources, + LIBS=libraries, + LINKFLAGS=env['CUSTOM_LDFLAGS']) -if 'uninstall' not in COMMAND_LINE_TARGETS: - env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], input_plugin) - env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) + # if the plugin links to libmapnik ensure it is built first + Depends(TARGET, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) + + if 'uninstall' not in COMMAND_LINE_TARGETS: + env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], TARGET) + env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) + +plugin_obj = { + 'LIBS': libraries, + 'SOURCES': plugin_sources, +} + +Return('plugin_obj') diff --git a/plugins/input/ogr/build.py b/plugins/input/ogr/build.py index 26512b856..881d20006 100644 --- a/plugins/input/ogr/build.py +++ b/plugins/input/ogr/build.py @@ -1,7 +1,7 @@ # # This file is part of Mapnik (c++ mapping toolkit) # -# Copyright (C) 2007 Artem Pavlenko, Jean-Francois Doyon +# Copyright (C) 2013 Artem Pavlenko # # Mapnik is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -17,50 +17,67 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # -# - +# Import ('plugin_base') Import ('env') -prefix = env['PREFIX'] +PLUGIN_NAME = 'ogr' plugin_env = plugin_base.Clone() -ogr_src = Split( +plugin_sources = Split( """ - ogr_converter.cpp - ogr_datasource.cpp - ogr_featureset.cpp - ogr_index_featureset.cpp - """ - ) - -plugin_env['LIBS'] = [env['PLUGINS']['ogr']['lib']] + %(PLUGIN_NAME)s_converter.cpp + %(PLUGIN_NAME)s_datasource.cpp + %(PLUGIN_NAME)s_featureset.cpp + %(PLUGIN_NAME)s_index_featureset.cpp + """ % locals() +) # Link Library to Dependencies -plugin_env['LIBS'].append('mapnik') -plugin_env['LIBS'].append(env['ICU_LIB_NAME']) -plugin_env['LIBS'].append('boost_system%s' % env['BOOST_APPEND']) -plugin_env['LIBS'].append('boost_filesystem%s' % env['BOOST_APPEND']) +libraries = [env['PLUGINS']['ogr']['lib']] +libraries.append(env['ICU_LIB_NAME']) +libraries.append('boost_system%s' % env['BOOST_APPEND']) +libraries.append('boost_filesystem%s' % env['BOOST_APPEND']) + +cxxflags = [] if env['RUNTIME_LINK'] == 'static': cmd = 'gdal-config --dep-libs' plugin_env.ParseConfig(cmd) - plugin_env['LIBS'].append('proj') + libraries.append('proj') if env.get('BOOST_LIB_VERSION_FROM_HEADER'): boost_version_from_header = int(env['BOOST_LIB_VERSION_FROM_HEADER'].split('_')[1]) if boost_version_from_header < 46: # avoid ubuntu issue with boost interprocess: # https://github.com/mapnik/mapnik/issues/1082 - plugin_env.Append(CXXFLAGS = '-fpermissive') + cxxflags.Append('-fpermissive') -input_plugin = plugin_env.SharedLibrary('../ogr', source=ogr_src, SHLIBPREFIX='', SHLIBSUFFIX='.input', LINKFLAGS=env['CUSTOM_LDFLAGS']) +plugin_env.Append(CXXFLAGS=cxxflags) -# if the plugin links to libmapnik ensure it is built first -Depends(input_plugin, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) +if env['PLUGIN_LINKING'] == 'shared': + libraries.append('mapnik') -if 'uninstall' not in COMMAND_LINE_TARGETS: - env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], input_plugin) - env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) + TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME, + SHLIBPREFIX='', + SHLIBSUFFIX='.input', + source=plugin_sources, + LIBS=libraries, + LINKFLAGS=env['CUSTOM_LDFLAGS']) + + # if the plugin links to libmapnik ensure it is built first + Depends(TARGET, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) + + if 'uninstall' not in COMMAND_LINE_TARGETS: + env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], TARGET) + env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) + +plugin_obj = { + 'CXXFLAGS': cxxflags, + 'LIBS': libraries, + 'SOURCES': plugin_sources, +} + +Return('plugin_obj') diff --git a/plugins/input/osm/build.py b/plugins/input/osm/build.py index 4cff88aea..f48a930d4 100644 --- a/plugins/input/osm/build.py +++ b/plugins/input/osm/build.py @@ -1,7 +1,7 @@ # # This file is part of Mapnik (c++ mapping toolkit) # -# Copyright (C) 2007 Artem Pavlenko, Jean-Francois Doyon +# Copyright (C) 2013 Artem Pavlenko # # Mapnik is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -17,38 +17,53 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # -# +# Import ('plugin_base') Import ('env') -prefix = env['PREFIX'] +PLUGIN_NAME = 'osm' plugin_env = plugin_base.Clone() -osm_src = Split( +plugin_sources = Split( """ + %(PLUGIN_NAME)s.cpp + %(PLUGIN_NAME)s_datasource.cpp + %(PLUGIN_NAME)s_featureset.cpp osmparser.cpp - osm.cpp - osm_datasource.cpp - osm_featureset.cpp dataset_deliverer.cpp basiccurl.cpp - """ - ) + """ % locals() +) +# Link Library to Dependencies libraries = [ 'xml2' ] libraries.append('curl') -libraries.append('mapnik') libraries.append(env['ICU_LIB_NAME']) libraries.append('boost_system%s' % env['BOOST_APPEND']) libraries.append('boost_filesystem%s' % env['BOOST_APPEND']) -input_plugin = plugin_env.SharedLibrary('../osm', source=osm_src, SHLIBPREFIX='', SHLIBSUFFIX='.input', LIBS=libraries, LINKFLAGS=env['CUSTOM_LDFLAGS']) +if env['PLUGIN_LINKING'] == 'shared': + libraries.append('mapnik') -# if the plugin links to libmapnik ensure it is built first -Depends(input_plugin, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) + TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME, + SHLIBPREFIX='', + SHLIBSUFFIX='.input', + source=plugin_sources, + LIBS=libraries, + LINKFLAGS=env['CUSTOM_LDFLAGS']) -if 'uninstall' not in COMMAND_LINE_TARGETS: - env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], input_plugin) - env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) + # if the plugin links to libmapnik ensure it is built first + Depends(TARGET, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) + + if 'uninstall' not in COMMAND_LINE_TARGETS: + env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], TARGET) + env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) + +plugin_obj = { + 'LIBS': libraries, + 'SOURCES': plugin_sources, +} + +Return('plugin_obj') diff --git a/plugins/input/postgis/build.py b/plugins/input/postgis/build.py index 271de7caf..2d31a3dc6 100644 --- a/plugins/input/postgis/build.py +++ b/plugins/input/postgis/build.py @@ -1,7 +1,7 @@ # # This file is part of Mapnik (c++ mapping toolkit) # -# Copyright (C) 2006 Artem Pavlenko, Jean-Francois Doyon +# Copyright (C) 2013 Artem Pavlenko # # Mapnik is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -17,44 +17,57 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # -# +# Import ('plugin_base') Import ('env') -prefix = env['PREFIX'] +PLUGIN_NAME = 'postgis' plugin_env = plugin_base.Clone() -postgis_src = Split( +plugin_sources = Split( """ - postgis_datasource.cpp - postgis_featureset.cpp - """ - ) - -# clear out and rebuild libs -plugin_env['LIBS'] = ['pq'] + %(PLUGIN_NAME)s_datasource.cpp + %(PLUGIN_NAME)s_featureset.cpp + """ % locals() +) # Link Library to Dependencies -plugin_env['LIBS'].append('mapnik') -plugin_env['LIBS'].append('boost_system%s' % env['BOOST_APPEND']) -plugin_env['LIBS'].append(env['ICU_LIB_NAME']) +libraries = ['pq'] +libraries.append('boost_system%s' % env['BOOST_APPEND']) +libraries.append(env['ICU_LIB_NAME']) + if env['THREADING'] == 'multi': - plugin_env['LIBS'].append('boost_thread%s' % env['BOOST_APPEND']) + libraries.append('boost_thread%s' % env['BOOST_APPEND']) if env['RUNTIME_LINK'] == 'static': #cmd = 'pg_config --libs' #plugin_env.ParseConfig(cmd) # pg_config does not seem to report correct deps of libpq # so resort to hardcoding for now - plugin_env['LIBS'].extend(['ldap','pam','ssl','crypto','krb5']) + libraries.extend(['ldap', 'pam', 'ssl', 'crypto', 'krb5']) -input_plugin = plugin_env.SharedLibrary('../postgis', source=postgis_src, SHLIBPREFIX='', SHLIBSUFFIX='.input', LINKFLAGS=env['CUSTOM_LDFLAGS']) +if env['PLUGIN_LINKING'] == 'shared': + libraries.append('mapnik') -# if the plugin links to libmapnik ensure it is built first -Depends(input_plugin, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) + TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME, + SHLIBPREFIX='', + SHLIBSUFFIX='.input', + source=plugin_sources, + LIBS=libraries, + LINKFLAGS=env['CUSTOM_LDFLAGS']) -if 'uninstall' not in COMMAND_LINE_TARGETS: - env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], input_plugin) - env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) + # if the plugin links to libmapnik ensure it is built first + Depends(TARGET, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) + + if 'uninstall' not in COMMAND_LINE_TARGETS: + env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], TARGET) + env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) + +plugin_obj = { + 'LIBS': libraries, + 'SOURCES': plugin_sources, +} + +Return('plugin_obj') diff --git a/plugins/input/python/build.py b/plugins/input/python/build.py index 78636adfc..bf5313eb4 100644 --- a/plugins/input/python/build.py +++ b/plugins/input/python/build.py @@ -1,13 +1,29 @@ -#!/usr/bin/env python +# +# This file is part of Mapnik (c++ mapping toolkit) +# +# Copyright (C) 2013 Artem Pavlenko +# +# Mapnik is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +# import os - -PLUGIN_NAME = 'python' - Import ('plugin_base') Import ('env') -install_dest = env['MAPNIK_INPUT_PLUGINS_DEST'] +PLUGIN_NAME = 'python' plugin_env = plugin_base.Clone() @@ -17,26 +33,32 @@ plugin_sources = Split( %(PLUGIN_NAME)s_featureset.cpp %(PLUGIN_NAME)s_utils.cpp """ % locals() - ) +) -boost_system = 'boost_system%s' % env['BOOST_APPEND'] -libraries = ['mapnik',env['BOOST_PYTHON_LIB'],boost_system,env['ICU_LIB_NAME']] +# Link Library to Dependencies +libraries = [] +libraries.append('boost_system%s' % env['BOOST_APPEND']) +libraries.append(env['BOOST_PYTHON_LIB']) +libraries.append(env['ICU_LIB_NAME']) +python_cpppath = env['PYTHON_INCLUDES'] +allcpp_paths = env['CPPPATH'] +allcpp_paths.extend(python_cpppath) # NOTE: explicit linking to libpython is uneeded on most linux version if the # python plugin is used by a app in python using mapnik's python bindings # we explicitly link to libpython here so that this plugin # can be used from a pure C++ calling application or a different binding language if env['PLATFORM'] == 'Darwin' and env['FRAMEWORK_PYTHON']: - if env['FRAMEWORK_SEARCH_PATH']: - python_link_flag = '-F%s -framework Python -Z' % env['FRAMEWORK_SEARCH_PATH'] + if env['FRAMEWORK_SEARCH_PATH']: + python_link_flag = '-F%s -framework Python -Z' % env['FRAMEWORK_SEARCH_PATH'] + else: + link_prefix = env['PYTHON_SYS_PREFIX'] + if '.framework' in link_prefix: + python_link_flag = '-F%s -framework Python -Z' % os.path.dirname(link_prefix.split('.')[0]) + elif '/System' in link_prefix: + python_link_flag = '-F/System/Library/Frameworks/ -framework Python -Z' else: - link_prefix = env['PYTHON_SYS_PREFIX'] - if '.framework' in link_prefix: - python_link_flag = '-F%s -framework Python -Z' % os.path.dirname(link_prefix.split('.')[0]) - elif '/System' in link_prefix: - python_link_flag = '-F/System/Library/Frameworks/ -framework Python -Z' - else: - python_link_flag = '-F/ -framework Python' + python_link_flag = '-F/ -framework Python' else: # on linux the linkflags end up to early in the compile flags to work correctly python_link_flag = '-L%s' % env['PYTHON_SYS_PREFIX'] + os.path.sep + env['LIBDIR_SCHEMA'] @@ -48,33 +70,31 @@ if env['CUSTOM_LDFLAGS']: else: linkflags = python_link_flag -plugin_env.Append(CPPPATH = env['PYTHON_INCLUDES']) - -TARGET = plugin_env.SharedLibrary( - # the name of the target to build, eg 'sqlite.input' - '../%s' % PLUGIN_NAME, - # prefix - normally none used - SHLIBPREFIX='', - # extension, mapnik expects '.input' - SHLIBSUFFIX='.input', - # list of source files to compile - source=plugin_sources, - # libraries to link to - LIBS=libraries, - # any custom linkflags, eg. LDFLAGS - # in this case CUSTOM_LDFLAGS comes - # from Mapnik's main SConstruct file - # and can be removed here if you do - # not need it - LINKFLAGS=linkflags - ) +if env['PLUGIN_LINKING'] == 'shared': + libraries.append('mapnik') + TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME, + SHLIBPREFIX='', + SHLIBSUFFIX='.input', + source=plugin_sources, + CPPPATH=allcpp_paths, + LIBS=libraries, + LINKFLAGS=linkflags) -# if the plugin links to libmapnik ensure it is built first -Depends(TARGET, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) + # if the plugin links to libmapnik ensure it is built first + Depends(TARGET, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) -# if 'uninstall' is not passed on the command line -# then we actually create the install targets that -# scons will install if 'install' is passed as an arg -if 'uninstall' not in COMMAND_LINE_TARGETS: - env.Install(install_dest, TARGET) - env.Alias('install', install_dest) + # if 'uninstall' is not passed on the command line + # then we actually create the install targets that + # scons will install if 'install' is passed as an arg + if 'uninstall' not in COMMAND_LINE_TARGETS: + env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], TARGET) + env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) + +plugin_obj = { + 'LIBS': libraries, + 'SOURCES': plugin_sources, + 'CPPPATH': python_cpppath, + 'LINKFLAGS': linkflags.replace('-Z','').split(' '), +} + +Return('plugin_obj') diff --git a/plugins/input/raster/build.py b/plugins/input/raster/build.py index 2a3c7dd41..b16028f68 100644 --- a/plugins/input/raster/build.py +++ b/plugins/input/raster/build.py @@ -1,7 +1,7 @@ # # This file is part of Mapnik (c++ mapping toolkit) # -# Copyright (C) 2006 Artem Pavlenko, Jean-Francois Doyon +# Copyright (C) 2013 Artem Pavlenko # # Mapnik is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -17,35 +17,49 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # -# +# Import ('plugin_base') Import ('env') -prefix = env['PREFIX'] +PLUGIN_NAME = 'raster' plugin_env = plugin_base.Clone() -raster_src = Split( +plugin_sources = Split( """ - raster_datasource.cpp - raster_featureset.cpp - raster_info.cpp - """ - ) + %(PLUGIN_NAME)s_datasource.cpp + %(PLUGIN_NAME)s_featureset.cpp + %(PLUGIN_NAME)s_info.cpp + """ % locals() +) -libraries = [] # Link Library to Dependencies -libraries.append('mapnik') +libraries = [] libraries.append(env['ICU_LIB_NAME']) libraries.append('boost_system%s' % env['BOOST_APPEND']) libraries.append('boost_filesystem%s' % env['BOOST_APPEND']) -input_plugin = plugin_env.SharedLibrary('../raster', source=raster_src, SHLIBPREFIX='', SHLIBSUFFIX='.input', LIBS=libraries, LINKFLAGS=env['CUSTOM_LDFLAGS']) +if env['PLUGIN_LINKING'] == 'shared': + libraries.append('mapnik') -# if the plugin links to libmapnik ensure it is built first -Depends(input_plugin, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) + TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME, + SHLIBPREFIX='', + SHLIBSUFFIX='.input', + source=plugin_sources, + LIBS=libraries, + LINKFLAGS=env['CUSTOM_LDFLAGS']) -if 'uninstall' not in COMMAND_LINE_TARGETS: - env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], input_plugin) - env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) + # if the plugin links to libmapnik ensure it is built first + Depends(TARGET, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) + + if 'uninstall' not in COMMAND_LINE_TARGETS: + env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], TARGET) + env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) + +plugin_obj = { + 'LIBS': libraries, + 'SOURCES': plugin_sources, +} + +Return('plugin_obj') diff --git a/plugins/input/rasterlite/build.py b/plugins/input/rasterlite/build.py index cee50da27..b3e6a834b 100644 --- a/plugins/input/rasterlite/build.py +++ b/plugins/input/rasterlite/build.py @@ -1,7 +1,7 @@ # # This file is part of Mapnik (c++ mapping toolkit) # -# Copyright (C) 2007 Artem Pavlenko, Jean-Francois Doyon +# Copyright (C) 2013 Artem Pavlenko # # Mapnik is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -17,35 +17,48 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # -# +# Import ('plugin_base') Import ('env') -prefix = env['PREFIX'] +PLUGIN_NAME = 'rasterlite' plugin_env = plugin_base.Clone() -rasterlite_src = Split( +plugin_sources = Split( """ - rasterlite_datasource.cpp - rasterlite_featureset.cpp - """ - ) - -libraries = [env['PLUGINS']['rasterlite']['lib']] + %(PLUGIN_NAME)s_datasource.cpp + %(PLUGIN_NAME)s_featureset.cpp + """ % locals() +) # Link Library to Dependencies -libraries.append('mapnik') +libraries = [env['PLUGINS']['rasterlite']['lib']] libraries.append(env['ICU_LIB_NAME']) libraries.append('boost_system%s' % env['BOOST_APPEND']) libraries.append('boost_filesystem%s' % env['BOOST_APPEND']) -input_plugin = plugin_env.SharedLibrary('../rasterlite', source=rasterlite_src, SHLIBPREFIX='', SHLIBSUFFIX='.input', LIBS=libraries, LINKFLAGS=env['CUSTOM_LDFLAGS']) +if env['PLUGIN_LINKING'] == 'shared': + libraries.append('mapnik') -# if the plugin links to libmapnik ensure it is built first -Depends(input_plugin, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) + TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME, + SHLIBPREFIX='', + SHLIBSUFFIX='.input', + source=plugin_sources, + LIBS=libraries, + LINKFLAGS=env['CUSTOM_LDFLAGS']) -if 'uninstall' not in COMMAND_LINE_TARGETS: - env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], input_plugin) - env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) + # if the plugin links to libmapnik ensure it is built first + Depends(TARGET, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) + + if 'uninstall' not in COMMAND_LINE_TARGETS: + env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], TARGET) + env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) + +plugin_obj = { + 'LIBS': libraries, + 'SOURCES': plugin_sources, +} + +Return('plugin_obj') diff --git a/plugins/input/shape/build.py b/plugins/input/shape/build.py index 5dd9995ac..d837b7fc8 100644 --- a/plugins/input/shape/build.py +++ b/plugins/input/shape/build.py @@ -1,7 +1,7 @@ - +# # This file is part of Mapnik (c++ mapping toolkit) # -# Copyright (C) 2006 Artem Pavlenko, Jean-Francois Doyon +# Copyright (C) 2013 Artem Pavlenko # # Mapnik is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -17,50 +17,70 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # -# - +# Import ('plugin_base') Import ('env') -prefix = env['PREFIX'] +PLUGIN_NAME = 'shape' plugin_env = plugin_base.Clone() -shape_src = Split( +plugin_sources = Split( """ - dbfile.cpp - shape_datasource.cpp - shape_featureset.cpp - shape_index_featureset.cpp - shape_io.cpp - shape_utils.cpp - """ - ) - -libraries = [] + %(PLUGIN_NAME)s_datasource.cpp + %(PLUGIN_NAME)s_featureset.cpp + %(PLUGIN_NAME)s_index_featureset.cpp + %(PLUGIN_NAME)s_io.cpp + %(PLUGIN_NAME)s_utils.cpp + dbfile.cpp + """ % locals() +) # Link Library to Dependencies -libraries.append('mapnik') +libraries = [] libraries.append(env['ICU_LIB_NAME']) libraries.append('boost_system%s' % env['BOOST_APPEND']) libraries.append('boost_filesystem%s' % env['BOOST_APPEND']) +cppdefines = [] +cxxflags = [] + if env['SHAPE_MEMORY_MAPPED_FILE']: - plugin_env.Append(CPPDEFINES = '-DSHAPE_MEMORY_MAPPED_FILE') + cppdefines.append('-DSHAPE_MEMORY_MAPPED_FILE') if env.get('BOOST_LIB_VERSION_FROM_HEADER'): boost_version_from_header = int(env['BOOST_LIB_VERSION_FROM_HEADER'].split('_')[1]) if boost_version_from_header < 46: # avoid ubuntu issue with boost interprocess: # https://github.com/mapnik/mapnik/issues/1082 - plugin_env.Append(CXXFLAGS = '-fpermissive') + cxxflags.append('-fpermissive') -input_plugin = plugin_env.SharedLibrary('../shape', SHLIBSUFFIX='.input', source=shape_src, SHLIBPREFIX='', LIBS = libraries, LINKFLAGS=env['CUSTOM_LDFLAGS']) +plugin_env.Append(CXXFLAGS=cxxflags) +plugin_env.Append(CPPDEFINES=cppdefines) -# if the plugin links to libmapnik ensure it is built first -Depends(input_plugin, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) +if env['PLUGIN_LINKING'] == 'shared': + libraries.append('mapnik') -if 'uninstall' not in COMMAND_LINE_TARGETS: - env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], input_plugin) - env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) + TARGET = plugin_env.SharedLibrary('../shape', + SHLIBSUFFIX='.input', + SHLIBPREFIX='', + source=plugin_sources, + LIBS=libraries, + LINKFLAGS=env['CUSTOM_LDFLAGS']) + + # if the plugin links to libmapnik ensure it is built first + Depends(TARGET, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) + + if 'uninstall' not in COMMAND_LINE_TARGETS: + env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], TARGET) + env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) + +plugin_obj = { + 'LIBS': libraries, + 'SOURCES': plugin_sources, + 'CXXFLAGS': cxxflags, + 'CPPDEFINES': cppdefines, +} + +Return('plugin_obj') diff --git a/plugins/input/sqlite/build.py b/plugins/input/sqlite/build.py index 458a885ad..e6de86d2c 100644 --- a/plugins/input/sqlite/build.py +++ b/plugins/input/sqlite/build.py @@ -1,7 +1,7 @@ # # This file is part of Mapnik (c++ mapping toolkit) # -# Copyright (C) 2007 Artem Pavlenko, Jean-Francois Doyon +# Copyright (C) 2013 Artem Pavlenko # # Mapnik is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -17,39 +17,54 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # -# +# Import ('plugin_base') Import ('env') -prefix = env['PREFIX'] +PLUGIN_NAME = 'sqlite' plugin_env = plugin_base.Clone() -sqlite_src = Split( +plugin_sources = Split( """ - sqlite_datasource.cpp - sqlite_featureset.cpp - """ - ) - -libraries = [ 'sqlite3' ] + %(PLUGIN_NAME)s_datasource.cpp + %(PLUGIN_NAME)s_featureset.cpp + """ % locals() +) # Link Library to Dependencies -libraries.append('mapnik') +libraries = [ 'sqlite3' ] libraries.append(env['ICU_LIB_NAME']) libraries.append('boost_system%s' % env['BOOST_APPEND']) libraries.append('boost_filesystem%s' % env['BOOST_APPEND']) -linkflags = env['CUSTOM_LDFLAGS'] +linkflags = [] if env['SQLITE_LINKFLAGS']: linkflags.append(env['SQLITE_LINKFLAGS']) -input_plugin = plugin_env.SharedLibrary('../sqlite', source=sqlite_src, SHLIBPREFIX='', SHLIBSUFFIX='.input', LIBS=libraries, LINKFLAGS=linkflags) +if env['PLUGIN_LINKING'] == 'shared': + libraries.append('mapnik') + linkflags.append(env['CUSTOM_LDFLAGS']) -# if the plugin links to libmapnik ensure it is built first -Depends(input_plugin, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) + TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME, + SHLIBPREFIX='', + SHLIBSUFFIX='.input', + source=plugin_sources, + LIBS=libraries, + LINKFLAGS=linkflags) -if 'uninstall' not in COMMAND_LINE_TARGETS: - env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], input_plugin) - env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) + # if the plugin links to libmapnik ensure it is built first + Depends(TARGET, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) + + if 'uninstall' not in COMMAND_LINE_TARGETS: + env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], TARGET) + env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST']) + +plugin_obj = { + 'LIBS': libraries, + 'SOURCES': plugin_sources, + 'LINKFLAGS': linkflags, +} + +Return('plugin_obj') diff --git a/plugins/input/templates/helloworld/build.py b/plugins/input/templates/helloworld/build.py index 5a974dd4b..d90efa26e 100644 --- a/plugins/input/templates/helloworld/build.py +++ b/plugins/input/templates/helloworld/build.py @@ -11,21 +11,17 @@ import os -# Give this plugin a name -# here this happens to be the same as the directory -PLUGIN_NAME = 'hello' - # Here we pull from the SCons environment exported from the main instance Import ('plugin_base') Import ('env') +# Give this plugin a name +# here this happens to be the same as the directory +PLUGIN_NAME = 'hello' + # the below install details are also pulled from the # main SConstruct file where configuration happens -# plugins can go anywhere, and be registered in custom locations by Mapnik -# but the standard location is '/usr/local/lib/mapnik/input' -install_dest = env['MAPNIK_INPUT_PLUGINS_DEST'] - # clone the environment here # so that if we modify the env it in this file # those changes to not pollute other builds later on... @@ -35,7 +31,7 @@ plugin_env = plugin_base.Clone() plugin_sources = Split( """ %(PLUGIN_NAME)s_datasource.cpp - %(PLUGIN_NAME)s_featureset.cpp + %(PLUGIN_NAME)s_featureset.cpp """ % locals() ) @@ -43,37 +39,54 @@ plugin_sources = Split( # directly link to libraries = [ '' ] # eg 'libfoo' -libraries.append('mapnik') libraries.append('boost_system%s' % env['BOOST_APPEND']) # link libicuuc, but ICU_LIB_NAME is used custom builds of icu can # have different library names like osx which offers /usr/lib/libicucore.dylib libraries.append(env['ICU_LIB_NAME']) - -TARGET = plugin_env.SharedLibrary( - # the name of the target to build, eg 'sqlite.input' - '../%s' % PLUGIN_NAME, - # prefix - normally none used - SHLIBPREFIX='', - # extension, mapnik expects '.input' - SHLIBSUFFIX='.input', - # list of source files to compile - source=plugin_sources, - # libraries to link to - LIBS=libraries, - # any custom linkflags, eg. LDFLAGS - # in this case CUSTOM_LDFLAGS comes - # from Mapnik's main SConstruct file - # and can be removed here if you do - # not need it - LINKFLAGS=env.get('CUSTOM_LDFLAGS') - ) -# if the plugin links to libmapnik ensure it is built first -Depends(TARGET, env.subst('../../../../src/%s' % env['MAPNIK_LIB_NAME'])) +# this is valid if we are building an external plugin as shared library +if env['PLUGIN_LINKING'] == 'shared': + # plugins can go anywhere, and be registered in custom locations by Mapnik + # but the standard location is '/usr/local/lib/mapnik/input' + install_dest = env['MAPNIK_INPUT_PLUGINS_DEST'] -# if 'uninstall' is not passed on the command line -# then we actually create the install targets that -# scons will install if 'install' is passed as an arg -if 'uninstall' not in COMMAND_LINE_TARGETS: - env.Install(install_dest, TARGET) - env.Alias('install', install_dest) + # only link mapnik if we are build an external shared object + libraries.append('mapnik') + + TARGET = plugin_env.SharedLibrary( + # the name of the target to build, eg 'sqlite.input' + '../%s' % PLUGIN_NAME, + # prefix - normally none used + SHLIBPREFIX='', + # extension, mapnik expects '.input' + SHLIBSUFFIX='.input', + # list of source files to compile + source=plugin_sources, + # libraries to link to + LIBS=libraries, + # any custom linkflags, eg. LDFLAGS + # in this case CUSTOM_LDFLAGS comes + # from Mapnik's main SConstruct file + # and can be removed here if you do + # not need it + LINKFLAGS=env.get('CUSTOM_LDFLAGS') + ) + + # if the plugin links to libmapnik ensure it is built first + Depends(TARGET, env.subst('../../../../src/%s' % env['MAPNIK_LIB_NAME'])) + + # if 'uninstall' is not passed on the command line + # then we actually create the install targets that + # scons will install if 'install' is passed as an arg + if 'uninstall' not in COMMAND_LINE_TARGETS: + env.Install(install_dest, TARGET) + env.Alias('install', install_dest) + +# Return the plugin building options to scons +# This is used when statically linking the plugin with mapnik) +plugin_obj = { + 'LIBS': libraries, + 'SOURCES': plugin_sources, +} + +Return('plugin_obj') diff --git a/src/build.py b/src/build.py index e6b9b9b8f..ecaa0f0d9 100644 --- a/src/build.py +++ b/src/build.py @@ -81,7 +81,6 @@ lib_env['LIBS'].append('xml2') if env['THREADING'] == 'multi': lib_env['LIBS'].append('boost_thread%s' % env['BOOST_APPEND']) - if env['RUNTIME_LINK'] == 'static': if 'icuuc' in env['ICU_LIB_NAME']: lib_env['LIBS'].append('icudata') @@ -124,6 +123,7 @@ source = Split( box2d.cpp building_symbolizer.cpp datasource_cache.cpp + datasource_cache_static.cpp debug.cpp deepcopy.cpp expression_node.cpp @@ -207,9 +207,31 @@ source = Split( """ ) +if env['PLUGIN_LINKING'] == 'static': + lib_env.Append(CPPDEFINES = '-DMAPNIK_STATIC_PLUGINS') + for plugin in env['REQUESTED_PLUGINS']: + details = env['PLUGINS'][plugin] + if details['lib'] in env['LIBS'] or not details['lib']: + lib_env.Append(CPPDEFINES = '-DMAPNIK_STATIC_PLUGIN_%s' % plugin.upper()) + plugin_env = SConscript('../plugins/input/%s/build.py' % plugin) + if plugin_env.has_key('SOURCES') and plugin_env['SOURCES']: + source += ['../plugins/input/%s/%s' % (plugin, src) for src in plugin_env['SOURCES']] + if plugin_env.has_key('CPPDEFINES') and plugin_env['CPPDEFINES']: + lib_env.AppendUnique(CPPDEFINES=plugin_env['CPPDEFINES']) + if plugin_env.has_key('CXXFLAGS') and plugin_env['CXXFLAGS']: + lib_env.AppendUnique(CXXFLAGS=plugin_env['CXXFLAGS']) + if plugin_env.has_key('LINKFLAGS') and plugin_env['LINKFLAGS']: + lib_env.AppendUnique(LINKFLAGS=plugin_env['LINKFLAGS']) + if plugin_env.has_key('CPPPATH') and plugin_env['CPPPATH']: + lib_env.AppendUnique(CPPPATH=copy(plugin_env['CPPPATH'])) + if plugin_env.has_key('LIBS') and plugin_env['LIBS']: + lib_env.AppendUnique(LIBS=plugin_env['LIBS']) + else: + print("Notice: dependencies not met for plugin '%s', not building..." % plugin) + if env['HAS_CAIRO']: lib_env.AppendUnique(LIBPATH=env['CAIRO_LIBPATHS']) - lib_env.Append(LIBS=env['CAIRO_LINKFLAGS']) + lib_env.Append(LIBS=env['CAIRO_ALL_LIBS']) lib_env.Append(CPPDEFINES = '-DHAVE_CAIRO') libmapnik_defines.append('-DHAVE_CAIRO') lib_env.AppendUnique(CPPPATH=copy(env['CAIRO_CPPPATHS'])) @@ -350,13 +372,17 @@ if env['RENDERING_STATS']: else: source.insert(0,processor_cpp); +# clone the env one more time to isolate mapnik_lib_link_flag +lib_env_final = lib_env.Clone() + if env['CUSTOM_LDFLAGS']: - linkflags = '%s %s' % (env['CUSTOM_LDFLAGS'], mapnik_lib_link_flag) + lib_env_final.Prepend(LINKFLAGS='%s %s' % (env['CUSTOM_LDFLAGS'], mapnik_lib_link_flag)) else: - linkflags = mapnik_lib_link_flag + lib_env_final.Prepend(LINKFLAGS=mapnik_lib_link_flag) # cache library values for other builds to use env['LIBMAPNIK_LIBS'] = copy(lib_env['LIBS']) +env['LIBMAPNIK_LINKFLAGS'] = copy(lib_env['LINKFLAGS']) env['LIBMAPNIK_CXXFLAGS'] = libmapnik_cxxflags env['LIBMAPNIK_DEFINES'] = libmapnik_defines @@ -366,9 +392,9 @@ if env['PLATFORM'] == 'Darwin': target_path = env['MAPNIK_LIB_BASE_DEST'] if 'uninstall' not in COMMAND_LINE_TARGETS: if env['LINKING'] == 'static': - mapnik = lib_env.StaticLibrary('mapnik', source, LINKFLAGS=linkflags) + mapnik = lib_env_final.StaticLibrary('mapnik', source) else: - mapnik = lib_env.SharedLibrary('mapnik', source, LINKFLAGS=linkflags) + mapnik = lib_env_final.SharedLibrary('mapnik', source) result = env.Install(target_path, mapnik) env.Alias(target='install', source=result) @@ -390,15 +416,14 @@ else: if 'uninstall' not in COMMAND_LINE_TARGETS: if env['LINKING'] == 'static': - mapnik = lib_env.StaticLibrary('mapnik', source, LINKFLAGS=linkflags) + mapnik = lib_env_final.StaticLibrary('mapnik', source) else: - mapnik = lib_env.SharedLibrary('mapnik', source, LINKFLAGS=linkflags) + mapnik = lib_env_final.SharedLibrary('mapnik', source) result = env.InstallAs(target=target, source=mapnik) env.Alias(target='install', source=result) if result: env.AddPostAction(result, ldconfig) - # Install symlinks target1 = os.path.join(env['MAPNIK_LIB_BASE_DEST'], "%s.%d.%d" % \ (os.path.basename(env.subst(env['MAPNIK_LIB_NAME'])),int(major), int(minor))) diff --git a/src/datasource_cache.cpp b/src/datasource_cache.cpp index 70e0f4636..e303d7d6e 100644 --- a/src/datasource_cache.cpp +++ b/src/datasource_cache.cpp @@ -38,6 +38,9 @@ namespace mapnik { +extern datasource_ptr create_static_datasource(parameters const& params); +extern std::vector get_static_datasource_names(); + bool is_input_plugin(std::string const& filename) { return boost::algorithm::ends_with(filename,std::string(".input")); @@ -62,13 +65,23 @@ datasource_ptr datasource_cache::create(parameters const& params) "parameter 'type' is missing"); } + datasource_ptr ds; + +#ifdef MAPNIK_STATIC_PLUGINS + // return if it's created, raise otherwise + ds = create_static_datasource(params); + if (ds) + { + return ds; + } +#endif + #ifdef MAPNIK_THREADSAFE mutex::scoped_lock lock(mutex_); #endif - datasource_ptr ds; std::map >::iterator itr=plugins_.find(*type); - if ( itr == plugins_.end() ) + if (itr == plugins_.end()) { std::string s("Could not create datasource for type: '"); s += *type + "'"; @@ -83,7 +96,7 @@ datasource_ptr datasource_cache::create(parameters const& params) throw config_error(s); } - if (!itr->second->valid()) + if (! itr->second->valid()) { throw std::runtime_error(std::string("Cannot load library: ") + itr->second->get_error()); @@ -95,13 +108,19 @@ datasource_ptr datasource_cache::create(parameters const& params) #endif create_ds* create_datasource = reinterpret_cast(itr->second->get_symbol("create")); - if (!create_datasource) + if (! create_datasource) { throw std::runtime_error(std::string("Cannot load symbols: ") + itr->second->get_error()); } + ds = datasource_ptr(create_datasource(params), datasource_deleter()); + #ifdef MAPNIK_LOG + MAPNIK_LOG_DEBUG(datasource_cache) + << "datasource_cache: Datasource=" + << ds << " type=" << type; + MAPNIK_LOG_DEBUG(datasource_cache) << "datasource_cache: Size=" << params.size(); @@ -115,12 +134,6 @@ datasource_ptr datasource_cache::create(parameters const& params) } #endif - ds = datasource_ptr(create_datasource(params), datasource_deleter()); - - MAPNIK_LOG_DEBUG(datasource_cache) - << "datasource_cache: Datasource=" - << ds << " type=" << type; - return ds; } @@ -132,11 +145,17 @@ std::string datasource_cache::plugin_directories() std::vector datasource_cache::plugin_names() { std::vector names; + +#ifdef MAPNIK_STATIC_PLUGINS + names = get_static_datasource_names(); +#endif + std::map >::const_iterator itr; - for (itr = plugins_.begin();itr!=plugins_.end();++itr) + for (itr = plugins_.begin(); itr != plugins_.end(); ++itr) { names.push_back(itr->first); } + return names; } @@ -145,6 +164,7 @@ void datasource_cache::register_datasources(std::string const& str) #ifdef MAPNIK_THREADSAFE mutex::scoped_lock lock(mutex_); #endif + boost::filesystem::path path(str); // TODO - only push unique paths plugin_directories_.push_back(str); @@ -152,24 +172,24 @@ void datasource_cache::register_datasources(std::string const& str) if (exists(path) && is_directory(path)) { - for (boost::filesystem::directory_iterator itr(path);itr!=end_itr;++itr ) + for (boost::filesystem::directory_iterator itr(path); itr != end_itr; ++itr ) { #if (BOOST_FILESYSTEM_VERSION == 3) - if (!is_directory( *itr ) && is_input_plugin(itr->path().filename().string())) + if (! is_directory(*itr) && is_input_plugin(itr->path().filename().string())) #else // v2 - if (!is_directory( *itr ) && is_input_plugin(itr->path().leaf())) + if (! is_directory(*itr) && is_input_plugin(itr->path().leaf())) +#endif + { +#if (BOOST_FILESYSTEM_VERSION == 3) + if (register_datasource(itr->path().string())) +#else // v2 + if (register_datasource(itr->string())) #endif { -#if (BOOST_FILESYSTEM_VERSION == 3) - if (register_datasource(itr->path().string())) -#else // v2 - if (register_datasource(itr->string())) -#endif - { - registered_ = true; - } + registered_ = true; } + } } } } @@ -206,9 +226,9 @@ bool datasource_cache::register_datasource(std::string const& filename) } catch (std::exception const& ex) { - MAPNIK_LOG_ERROR(datasource_cache) - << "Exception caught while loading plugin library: " - << filename << " (" << ex.what() << ")"; + MAPNIK_LOG_ERROR(datasource_cache) + << "Exception caught while loading plugin library: " + << filename << " (" << ex.what() << ")"; } return success; } diff --git a/utils/mapnik-config/build.py b/utils/mapnik-config/build.py index deef156a7..6b94f3c37 100644 --- a/utils/mapnik-config/build.py +++ b/utils/mapnik-config/build.py @@ -52,6 +52,7 @@ if config_env['HAS_CAIRO']: dep_includes += ''.join([' -I%s' % i for i in env['CAIRO_CPPPATHS'] if not i.startswith('#')]) ldflags = config_env['CUSTOM_LDFLAGS'] + ''.join([' -L%s' % i for i in config_env['LIBPATH'] if not i.startswith('#')]) +ldflags += config_env['LIBMAPNIK_LINKFLAGS'] dep_libs = ''.join([' -l%s' % i for i in env['LIBMAPNIK_LIBS']]) From 5a6ea9ee6f297d64e0f699f362de25f7e3b2743f Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 16 May 2013 11:54:56 -0700 Subject: [PATCH 014/110] finish to rename of mapnik::Feature -> mapnik::feature_impl --- bindings/python/build.py | 2 +- bindings/python/mapnik_expression.cpp | 12 +++--- bindings/python/mapnik_feature.cpp | 38 +++++++++---------- bindings/python/mapnik_featureset.cpp | 5 +-- bindings/python/mapnik_rule.cpp | 1 - bindings/python/mapnik_text_placement.cpp | 18 ++++----- include/mapnik/feature.hpp | 3 +- include/mapnik/feature_factory.hpp | 8 ++-- .../mapnik/feature_style_processor_impl.hpp | 2 +- include/mapnik/hit_test_filter.hpp | 2 +- include/mapnik/raster_colorizer.hpp | 7 +++- include/mapnik/symbolizer_helpers.hpp | 6 +-- 12 files changed, 50 insertions(+), 54 deletions(-) diff --git a/bindings/python/build.py b/bindings/python/build.py index 44d8c1376..e32c9a410 100644 --- a/bindings/python/build.py +++ b/bindings/python/build.py @@ -1,7 +1,7 @@ # # This file is part of Mapnik (c++ mapping toolkit) # -# Copyright (C) 2006 Artem Pavlenko, Jean-Francois Doyon +# Copyright (C) 2013 Artem Pavlenko # # Mapnik is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public diff --git a/bindings/python/mapnik_expression.cpp b/bindings/python/mapnik_expression.cpp index d3bd8b1dd..caf4d2525 100644 --- a/bindings/python/mapnik_expression.cpp +++ b/bindings/python/mapnik_expression.cpp @@ -34,8 +34,6 @@ #include #include - -using mapnik::Feature; using mapnik::expression_ptr; using mapnik::parse_expression; using mapnik::to_expression_string; @@ -53,15 +51,15 @@ std::string expression_to_string_(mapnik::expr_node const& expr) return mapnik::to_expression_string(expr); } -mapnik::value expression_evaluate_(mapnik::expr_node const& expr, mapnik::Feature const& f) +mapnik::value expression_evaluate_(mapnik::expr_node const& expr, mapnik::feature_impl const& f) { // will be auto-converted to proper python type by `mapnik_value_to_python` - return boost::apply_visitor(mapnik::evaluate(f),expr); + return boost::apply_visitor(mapnik::evaluate(f),expr); } -bool expression_evaluate_to_bool_(mapnik::expr_node const& expr, mapnik::Feature const& f) +bool expression_evaluate_to_bool_(mapnik::expr_node const& expr, mapnik::feature_impl const& f) { - return boost::apply_visitor(mapnik::evaluate(f),expr).to_bool(); + return boost::apply_visitor(mapnik::evaluate(f),expr).to_bool(); } // path expression @@ -75,7 +73,7 @@ std::string path_to_string_(mapnik::path_expression const& expr) return mapnik::path_processor_type::to_string(expr); } -std::string path_evaluate_(mapnik::path_expression const& expr, mapnik::Feature const& f) +std::string path_evaluate_(mapnik::path_expression const& expr, mapnik::feature_impl const& f) { return mapnik::path_processor_type::evaluate(expr, f); } diff --git a/bindings/python/mapnik_feature.cpp b/bindings/python/mapnik_feature.cpp index 0c7e6745e..8f4e4494f 100644 --- a/bindings/python/mapnik_feature.cpp +++ b/bindings/python/mapnik_feature.cpp @@ -41,28 +41,27 @@ namespace { -using mapnik::Feature; using mapnik::geometry_utils; using mapnik::from_wkt; using mapnik::context_type; using mapnik::context_ptr; using mapnik::feature_kv_iterator; -mapnik::geometry_type const& (mapnik::Feature::*get_geometry_by_const_ref)(unsigned) const = &mapnik::Feature::get_geometry; -boost::ptr_vector const& (mapnik::Feature::*get_paths_by_const_ref)() const = &mapnik::Feature::paths; +mapnik::geometry_type const& (mapnik::feature_impl::*get_geometry_by_const_ref)(unsigned) const = &mapnik::feature_impl::get_geometry; +boost::ptr_vector const& (mapnik::feature_impl::*get_paths_by_const_ref)() const = &mapnik::feature_impl::paths; -void feature_add_geometries_from_wkb(Feature &feature, std::string wkb) +void feature_add_geometries_from_wkb(mapnik::feature_impl &feature, std::string wkb) { geometry_utils::from_wkb(feature.paths(), wkb.c_str(), wkb.size()); } -void feature_add_geometries_from_wkt(Feature &feature, std::string wkt) +void feature_add_geometries_from_wkt(mapnik::feature_impl &feature, std::string wkt) { bool result = mapnik::from_wkt(wkt, feature.paths()); if (!result) throw std::runtime_error("Failed to parse WKT"); } -std::string feature_to_geojson(Feature const& feature) +std::string feature_to_geojson(mapnik::feature_impl const& feature) { std::string json; mapnik::json::feature_generator g; @@ -73,22 +72,22 @@ std::string feature_to_geojson(Feature const& feature) return json; } -mapnik::value __getitem__(Feature const& feature, std::string const& name) +mapnik::value __getitem__(mapnik::feature_impl const& feature, std::string const& name) { return feature.get(name); } -mapnik::value __getitem2__(Feature const& feature, std::size_t index) +mapnik::value __getitem2__(mapnik::feature_impl const& feature, std::size_t index) { return feature.get(index); } -void __setitem__(Feature & feature, std::string const& name, mapnik::value const& val) +void __setitem__(mapnik::feature_impl & feature, std::string const& name, mapnik::value const& val) { feature.put_new(name,val); } -boost::python::dict attributes(Feature const& f) +boost::python::dict attributes(mapnik::feature_impl const& f) { boost::python::dict attributes; feature_kv_iterator itr = f.begin(); @@ -191,7 +190,6 @@ struct value_null_from_python void export_feature() { using namespace boost::python; - using mapnik::Feature; // Python to mapnik::value converters // NOTE: order matters here. For example value_null must be listed before @@ -211,25 +209,25 @@ void export_feature() .def("push", &context_type::push) ; - class_, + class_, boost::noncopyable>("Feature",init("Default ctor.")) - .def("id",&Feature::id) - .def("__str__",&Feature::to_string) + .def("id",&mapnik::feature_impl::id) + .def("__str__",&mapnik::feature_impl::to_string) .def("add_geometries_from_wkb", &feature_add_geometries_from_wkb) .def("add_geometries_from_wkt", &feature_add_geometries_from_wkt) - .def("add_geometry", &Feature::add_geometry) - .def("num_geometries",&Feature::num_geometries) + .def("add_geometry", &mapnik::feature_impl::add_geometry) + .def("num_geometries",&mapnik::feature_impl::num_geometries) .def("get_geometry", make_function(get_geometry_by_const_ref,return_value_policy())) .def("geometries",make_function(get_paths_by_const_ref,return_value_policy())) - .def("envelope", &Feature::envelope) - .def("has_key", &Feature::has_key) + .def("envelope", &mapnik::feature_impl::envelope) + .def("has_key", &mapnik::feature_impl::has_key) .add_property("attributes",&attributes) .def("__setitem__",&__setitem__) .def("__contains__",&__getitem__) .def("__getitem__",&__getitem__) .def("__getitem__",&__getitem2__) - .def("__len__", &Feature::size) - .def("context",&Feature::context) + .def("__len__", &mapnik::feature_impl::size) + .def("context",&mapnik::feature_impl::context) .def("to_geojson",&feature_to_geojson) ; } diff --git a/bindings/python/mapnik_featureset.cpp b/bindings/python/mapnik_featureset.cpp index 3148d116e..c1f7eecd4 100644 --- a/bindings/python/mapnik_featureset.cpp +++ b/bindings/python/mapnik_featureset.cpp @@ -65,10 +65,7 @@ inline mapnik::feature_ptr next(mapnik::featureset_ptr const& itr) void export_featureset() { using namespace boost::python; - using mapnik::Feature; - using mapnik::Featureset; - - class_, + class_, boost::noncopyable>("Featureset",no_init) .def("__iter__",pass_through) .def("next",next) diff --git a/bindings/python/mapnik_rule.cpp b/bindings/python/mapnik_rule.cpp index bad1f2639..80341ca24 100644 --- a/bindings/python/mapnik_rule.cpp +++ b/bindings/python/mapnik_rule.cpp @@ -34,7 +34,6 @@ using mapnik::rule; using mapnik::expr_node; using mapnik::expression_ptr; -using mapnik::Feature; using mapnik::point_symbolizer; using mapnik::line_symbolizer; using mapnik::line_pattern_symbolizer; diff --git a/bindings/python/mapnik_text_placement.cpp b/bindings/python/mapnik_text_placement.cpp index f1dcefae8..05201326a 100644 --- a/bindings/python/mapnik_text_placement.cpp +++ b/bindings/python/mapnik_text_placement.cpp @@ -84,7 +84,7 @@ struct NodeWrap: formatting::node, wrapper } - void apply(char_properties const& p, Feature const& feature, processed_text &output) const + void apply(char_properties const& p, feature_impl const& feature, processed_text &output) const { python_block_auto_unblock b; this->get_override("apply")(ptr(&p), ptr(&feature), ptr(&output)); @@ -122,7 +122,7 @@ struct TextNodeWrap: formatting::text_node, wrapper } - virtual void apply(char_properties const& p, Feature const& feature, processed_text &output) const + virtual void apply(char_properties const& p, feature_impl const& feature, processed_text &output) const { if(override o = this->get_override("apply")) { @@ -135,7 +135,7 @@ struct TextNodeWrap: formatting::text_node, wrapper } } - void default_apply(char_properties const& p, Feature const& feature, processed_text &output) const + void default_apply(char_properties const& p, feature_impl const& feature, processed_text &output) const { formatting::text_node::apply(p, feature, output); } @@ -143,7 +143,7 @@ struct TextNodeWrap: formatting::text_node, wrapper struct FormatNodeWrap: formatting::format_node, wrapper { - virtual void apply(char_properties const& p, Feature const& feature, processed_text &output) const + virtual void apply(char_properties const& p, feature_impl const& feature, processed_text &output) const { if(override o = this->get_override("apply")) { @@ -156,7 +156,7 @@ struct FormatNodeWrap: formatting::format_node, wrapper } } - void default_apply(char_properties const& p, Feature const& feature, processed_text &output) const + void default_apply(char_properties const& p, feature_impl const& feature, processed_text &output) const { formatting::format_node::apply(p, feature, output); } @@ -164,7 +164,7 @@ struct FormatNodeWrap: formatting::format_node, wrapper struct ExprFormatWrap: formatting::expression_format, wrapper { - virtual void apply(char_properties const& p, Feature const& feature, processed_text &output) const + virtual void apply(char_properties const& p, feature_impl const& feature, processed_text &output) const { if(override o = this->get_override("apply")) { @@ -177,7 +177,7 @@ struct ExprFormatWrap: formatting::expression_format, wrapper http://wiki.python.org/moin/boost.python/HowTo#A.22Raw.22_function */ - virtual void apply(char_properties const& p, Feature const& feature, processed_text &output) const + virtual void apply(char_properties const& p, feature_impl const& feature, processed_text &output) const { if(override o = this->get_override("apply")) { @@ -214,7 +214,7 @@ struct ListNodeWrap: formatting::list_node, wrapper } } - void default_apply(char_properties const& p, Feature const& feature, processed_text &output) const + void default_apply(char_properties const& p, feature_impl const& feature, processed_text &output) const { formatting::list_node::apply(p, feature, output); } diff --git a/include/mapnik/feature.hpp b/include/mapnik/feature.hpp index 109dadab9..ce9a3dc79 100644 --- a/include/mapnik/feature.hpp +++ b/include/mapnik/feature.hpp @@ -306,9 +306,10 @@ inline std::ostream& operator<< (std::ostream & out,feature_impl const& f) return out; } +// TODO - remove at Mapnik 3.x typedef feature_impl Feature; -typedef boost::shared_ptr feature_ptr; +typedef boost::shared_ptr feature_ptr; } diff --git a/include/mapnik/feature_factory.hpp b/include/mapnik/feature_factory.hpp index 9a6574932..7896ef04b 100644 --- a/include/mapnik/feature_factory.hpp +++ b/include/mapnik/feature_factory.hpp @@ -35,11 +35,11 @@ namespace mapnik { struct feature_factory { - static boost::shared_ptr create (context_ptr const& ctx, mapnik::value_integer fid) + static boost::shared_ptr create (context_ptr const& ctx, mapnik::value_integer fid) { - //return boost::allocate_shared(boost::pool_allocator(),fid); - //return boost::allocate_shared(boost::fast_pool_allocator(),fid); - return boost::make_shared(ctx,fid); + //return boost::allocate_shared(boost::pool_allocator(),fid); + //return boost::allocate_shared(boost::fast_pool_allocator(),fid); + return boost::make_shared(ctx,fid); } }; } diff --git a/include/mapnik/feature_style_processor_impl.hpp b/include/mapnik/feature_style_processor_impl.hpp index eade195b3..bd7653ae2 100644 --- a/include/mapnik/feature_style_processor_impl.hpp +++ b/include/mapnik/feature_style_processor_impl.hpp @@ -606,7 +606,7 @@ void feature_style_processor::render_style( BOOST_FOREACH(rule const* r, rc.get_if_rules() ) { expression_ptr const& expr=r->get_filter(); - value_type result = boost::apply_visitor(evaluate(*feature),*expr); + value_type result = boost::apply_visitor(evaluate(*feature),*expr); if (result.to_bool()) { #if defined(RENDERING_STATS) diff --git a/include/mapnik/hit_test_filter.hpp b/include/mapnik/hit_test_filter.hpp index 792c8c12e..b9c1f6a6b 100644 --- a/include/mapnik/hit_test_filter.hpp +++ b/include/mapnik/hit_test_filter.hpp @@ -38,7 +38,7 @@ public: y_(y), tol_(tol) {} - bool pass(Feature & feature) + bool pass(feature_impl & feature) { BOOST_FOREACH(geometry_type & geom, feature.paths()) { diff --git a/include/mapnik/raster_colorizer.hpp b/include/mapnik/raster_colorizer.hpp index 806af3602..76c09fd94 100644 --- a/include/mapnik/raster_colorizer.hpp +++ b/include/mapnik/raster_colorizer.hpp @@ -40,7 +40,6 @@ // mapnik #include #include -#include #include // boost @@ -52,6 +51,10 @@ namespace mapnik { +class feature_impl; +class raster; + + //! \brief Enumerates the modes of interpolation enum colorizer_mode_enum { @@ -197,7 +200,7 @@ public: //! //! \param[in, out] raster A raster stored in float32 single channel format, which gets colorized in place. //! \param[in] f The feature used to find 'NODATA' information if available - void colorize(raster_ptr const& raster, Feature const& f) const; + void colorize(boost::shared_ptr const& raster, feature_impl const& f) const; //! \brief Perform the translation of input to output diff --git a/include/mapnik/symbolizer_helpers.hpp b/include/mapnik/symbolizer_helpers.hpp index c6bc585c2..4da83c360 100644 --- a/include/mapnik/symbolizer_helpers.hpp +++ b/include/mapnik/symbolizer_helpers.hpp @@ -57,7 +57,7 @@ class text_symbolizer_helper { public: text_symbolizer_helper(text_symbolizer const& sym, - Feature const& feature, + feature_impl const& feature, proj_transform const& prj_trans, unsigned width, unsigned height, @@ -104,7 +104,7 @@ protected: //Input text_symbolizer const& sym_; - Feature const& feature_; + feature_impl const& feature_; proj_transform const& prj_trans_; CoordTransform const& t_; FaceManagerT & font_manager_; @@ -142,7 +142,7 @@ class shield_symbolizer_helper: public text_symbolizer_helper Date: Thu, 16 May 2013 12:03:30 -0700 Subject: [PATCH 015/110] add missing file from 3fbf4df67d78dbf7f69bd05fe7d0bc199f73d632 --- src/datasource_cache_static.cpp | 171 ++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 src/datasource_cache_static.cpp diff --git a/src/datasource_cache_static.cpp b/src/datasource_cache_static.cpp new file mode 100644 index 000000000..02ee005e5 --- /dev/null +++ b/src/datasource_cache_static.cpp @@ -0,0 +1,171 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2013 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +// mapnik +#include + +#ifdef MAPNIK_STATIC_PLUGINS +#include + +// boost +#include +#include +#include +#endif + +// stl +#include + +// static plugin linkage +#ifdef MAPNIK_STATIC_PLUGINS + #if defined(MAPNIK_STATIC_PLUGIN_CSV) + #include "plugins/input/csv/csv_datasource.hpp" + #endif + #if defined(MAPNIK_STATIC_PLUGIN_GDAL) + #include "plugins/input/gdal/gdal_datasource.hpp" + #endif + #if defined(MAPNIK_STATIC_PLUGIN_GEOJSON) + #include "plugins/input/geojson/geojson_datasource.hpp" + #endif + #if defined(MAPNIK_STATIC_PLUGIN_GEOS) + #include "plugins/input/geos/geos_datasource.hpp" + #endif + #if defined(MAPNIK_STATIC_PLUGIN_KISMET) + #include "plugins/input/kismet/kismet_datasource.hpp" + #endif + #if defined(MAPNIK_STATIC_PLUGIN_OCCI) + #include "plugins/input/occi/occi_datasource.hpp" + #endif + #if defined(MAPNIK_STATIC_PLUGIN_OGR) + #include "plugins/input/ogr/ogr_datasource.hpp" + #endif + #if defined(MAPNIK_STATIC_PLUGIN_OSM) + #include "plugins/input/osm/osm_datasource.hpp" + #endif + #if defined(MAPNIK_STATIC_PLUGIN_POSTGIS) + #include "plugins/input/postgis/postgis_datasource.hpp" + #endif + #if defined(MAPNIK_STATIC_PLUGIN_PYTHON) + #include "plugins/input/python/python_datasource.hpp" + #endif + #if defined(MAPNIK_STATIC_PLUGIN_RASTER) + #include "plugins/input/raster/raster_datasource.hpp" + #endif + #if defined(MAPNIK_STATIC_PLUGIN_RASTERLITE) + #include "plugins/input/rasterlite/rasterlite_datasource.hpp" + #endif + #if defined(MAPNIK_STATIC_PLUGIN_SHAPE) + #include "plugins/input/shape/shape_datasource.hpp" + #endif + #if defined(MAPNIK_STATIC_PLUGIN_SQLITE) + #include "plugins/input/sqlite/sqlite_datasource.hpp" + #endif +#endif + +namespace mapnik { + +#ifdef MAPNIK_STATIC_PLUGINS +template +datasource_ptr ds_generator(parameters const& params) +{ + return boost::make_shared(params); +} + +typedef datasource_ptr (*ds_generator_ptr)(parameters const& params); +typedef boost::unordered::unordered_map datasource_map; + +static datasource_map ds_map = boost::assign::map_list_of + #if defined(MAPNIK_STATIC_PLUGIN_CSV) + (std::string("csv"), &ds_generator) + #endif + #if defined(MAPNIK_STATIC_PLUGIN_GDAL) + (std::string("gdal"), &ds_generator) + #endif + #if defined(MAPNIK_STATIC_PLUGIN_GEOJSON) + (std::string("geojson"), &ds_generator) + #endif + #if defined(MAPNIK_STATIC_PLUGIN_OCCI) + (std::string("occi"), &ds_generator) + #endif + #if defined(MAPNIK_STATIC_PLUGIN_OGR) + (std::string("ogr"), &ds_generator) + #endif + #if defined(MAPNIK_STATIC_PLUGIN_OSM) + (std::string("osm"), &ds_generator) + #endif + #if defined(MAPNIK_STATIC_PLUGIN_POSTGIS) + (std::string("postgis"), &ds_generator) + #endif + #if defined(MAPNIK_STATIC_PLUGIN_PYTHON) + (std::string("python"), &ds_generator) + #endif + #if defined(MAPNIK_STATIC_PLUGIN_RASTER) + (std::string("raster"), &ds_generator) + #endif + #if defined(MAPNIK_STATIC_PLUGIN_RASTERLITE) + (std::string("rasterlite"), &ds_generator) + #endif + #if defined(MAPNIK_STATIC_PLUGIN_SHAPE) + (std::string("shape"), &ds_generator) + #endif + #if defined(MAPNIK_STATIC_PLUGIN_SQLITE) + (std::string("sqlite"), &ds_generator) + #endif +; +#endif + +datasource_ptr create_static_datasource(parameters const& params) +{ + datasource_ptr ds; + +#ifdef MAPNIK_STATIC_PLUGINS + boost::optional type = params.get("type"); + + datasource_map::iterator it = ds_map.find(*type); + + if (it != ds_map.end()) + { + ds = it->second(params); + } +#endif + + return ds; +} + +std::vector get_static_datasource_names() +{ + std::vector names; + +#ifdef MAPNIK_STATIC_PLUGINS + datasource_map::iterator it = ds_map.begin(); + while (it != ds_map.end()) + { + names.push_back(it->first); + + it++; + } +#endif + + return names; +} + +} From c84ddc825e4105445de4ad0944b4a60da6390b3f Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 16 May 2013 12:23:19 -0700 Subject: [PATCH 016/110] convert old tests into proper python tests - closes #1460 --- demo/data/test/raw/charplacement.gfs | 25 --- demo/data/test/raw/charplacement.gml | 99 ---------- demo/data/test/raw/charplacement.xsd | 36 ---- demo/data/test/raw/displacement.gfs | 25 --- demo/data/test/raw/displacement.gml | 173 ------------------ demo/data/test/raw/displacement.xsd | 36 ---- demo/data/test/raw/overlap.gfs | 25 --- demo/data/test/raw/overlap.gml | 127 ------------- demo/data/test/raw/overlap.xsd | 36 ---- demo/data/test/raw/textspacing.gfs | 25 --- demo/data/test/raw/textspacing.gml | 117 ------------ demo/data/test/raw/textspacing.xsd | 36 ---- demo/data/test/readme.txt | 3 - demo/data/test/regenerate.sh | 20 -- .../test => tests/data/shp}/charplacement.dbf | Bin .../test => tests/data/shp}/charplacement.shp | Bin .../test => tests/data/shp}/charplacement.shx | Bin .../test => tests/data/shp}/displacement.dbf | Bin .../test => tests/data/shp}/displacement.shp | Bin .../test => tests/data/shp}/displacement.shx | Bin .../data/test => tests/data/shp}/overlap.dbf | Bin .../data/test => tests/data/shp}/overlap.shp | Bin .../data/test => tests/data/shp}/overlap.shx | Bin .../test => tests/data/shp}/textspacing.dbf | Bin .../test => tests/data/shp}/textspacing.shp | Bin .../test => tests/data/shp}/textspacing.shx | Bin .../images/support/char_placement.png | Bin 0 -> 71821 bytes .../images/support/displacement.png | Bin 0 -> 42357 bytes tests/python_tests/images/support/overlap.png | Bin 0 -> 49517 bytes tests/python_tests/images/support/spacing.png | Bin 0 -> 48120 bytes 30 files changed, 783 deletions(-) delete mode 100644 demo/data/test/raw/charplacement.gfs delete mode 100644 demo/data/test/raw/charplacement.gml delete mode 100644 demo/data/test/raw/charplacement.xsd delete mode 100644 demo/data/test/raw/displacement.gfs delete mode 100644 demo/data/test/raw/displacement.gml delete mode 100644 demo/data/test/raw/displacement.xsd delete mode 100644 demo/data/test/raw/overlap.gfs delete mode 100644 demo/data/test/raw/overlap.gml delete mode 100644 demo/data/test/raw/overlap.xsd delete mode 100644 demo/data/test/raw/textspacing.gfs delete mode 100644 demo/data/test/raw/textspacing.gml delete mode 100644 demo/data/test/raw/textspacing.xsd delete mode 100644 demo/data/test/readme.txt delete mode 100755 demo/data/test/regenerate.sh rename {demo/data/test => tests/data/shp}/charplacement.dbf (100%) rename {demo/data/test => tests/data/shp}/charplacement.shp (100%) rename {demo/data/test => tests/data/shp}/charplacement.shx (100%) rename {demo/data/test => tests/data/shp}/displacement.dbf (100%) rename {demo/data/test => tests/data/shp}/displacement.shp (100%) rename {demo/data/test => tests/data/shp}/displacement.shx (100%) rename {demo/data/test => tests/data/shp}/overlap.dbf (100%) rename {demo/data/test => tests/data/shp}/overlap.shp (100%) rename {demo/data/test => tests/data/shp}/overlap.shx (100%) rename {demo/data/test => tests/data/shp}/textspacing.dbf (100%) rename {demo/data/test => tests/data/shp}/textspacing.shp (100%) rename {demo/data/test => tests/data/shp}/textspacing.shx (100%) create mode 100644 tests/python_tests/images/support/char_placement.png create mode 100644 tests/python_tests/images/support/displacement.png create mode 100644 tests/python_tests/images/support/overlap.png create mode 100644 tests/python_tests/images/support/spacing.png diff --git a/demo/data/test/raw/charplacement.gfs b/demo/data/test/raw/charplacement.gfs deleted file mode 100644 index b05ad940b..000000000 --- a/demo/data/test/raw/charplacement.gfs +++ /dev/null @@ -1,25 +0,0 @@ - - - charplacement - charplacement - - 1 - 1.00000 - 2.00000 - 1.00000 - 5.00000 - - - NAME - NAME - String - 0 - - - CLASS - CLASS - String - 0 - - - diff --git a/demo/data/test/raw/charplacement.gml b/demo/data/test/raw/charplacement.gml deleted file mode 100644 index 8de776840..000000000 --- a/demo/data/test/raw/charplacement.gml +++ /dev/null @@ -1,99 +0,0 @@ - - - - - 00 - 13-13 - - - - - - 2,-1 1,-3 2,-5 - Road Name - TRIANGLE - - - 3,-1 4,-3 3,-5 - Road Name - TRIANGLE - - - - - 1,-12 13,-12 - - Road Name - STRAIGHT - - - - 1,-13 2,-13 5,-13 10,-13 13,-13 - - Road Name - STRAIGHT - - - - 6,-1 5,-3 5,-5 - Road Name - BEND - - - 7,-1 8,-3 8,-5 - Road Name - BEND - - - 5,-6 5,-8 6,-10 - Road Name - BEND - - - 8,-6 8,-8 7,-10 - Road Name - BEND - - - - 10.055915,-1.00031738281 10.6649858,-1.077712483 11.274056,-1.26950068 11.77921,-1.55298308 12.191993,-1.92815928 12.51529,-2.369132 12.746218,-2.8329032 12.884774,-3.2968745 12.930959,-3.875339 - Road Name - CURVE - - - 10.0555,-8.875339 10.6645708,-8.7979439 11.273641,-8.6061557 11.778795,-8.3226733 12.191578,-7.9474971 12.514875,-7.5065244 12.745803,-7.0427532 12.884359,-6.5787819 12.930544,-6.0003174 - Road Name - CURVE - - - - -9.055915,-2.00031738281 9.6649858,-2.077712483 10.274056,-2.26950068 10.77921,-2.55298308 11.191993,-2.92815928 11.51529,-3.369132 11.746218,-3.8329032 11.884774,-4.2968745 11.930959,-4.875339 -11.930544,-5.0003174 11.884359,-5.5787819 11.745803,-6.0427532 11.514875,-6.5065244 11.191578,-6.9474971 10.778795,-7.3226733 10.273641,-7.6061557 9.6645708,-7.7979439 9.0555,-7.875339 - - Road Name - CURVE - - - - -9.0435048,-10.5550195 9.480786,-10.2191668 9.963148,-10.0731439 10.540222,-10.2495527 10.968444,-10.525815 11.419238,-10.8336443 12.01882,-10.9565825 12.559787,-10.7996079 12.956495,-10.4089966 - - Road Name - SQUIGGLE - - - - - 1,-9 1.4,-10 1.8,-9 2.2,-10 2.6,-9 3.0,-10 3.4,-9 3.8,-10 4.2,-9 4.6,-10 - - Long ZigZag Road Name - ZIGZAG - - - - diff --git a/demo/data/test/raw/charplacement.xsd b/demo/data/test/raw/charplacement.xsd deleted file mode 100644 index 345905d06..000000000 --- a/demo/data/test/raw/charplacement.xsd +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/demo/data/test/raw/displacement.gfs b/demo/data/test/raw/displacement.gfs deleted file mode 100644 index 3034d7109..000000000 --- a/demo/data/test/raw/displacement.gfs +++ /dev/null @@ -1,25 +0,0 @@ - - - displacement - displacement - - 1 - 1.00000 - 2.00000 - 1.00000 - 5.00000 - - - NAME - NAME - String - 0 - - - CLASS - CLASS - String - 0 - - - diff --git a/demo/data/test/raw/displacement.gml b/demo/data/test/raw/displacement.gml deleted file mode 100644 index 9b3a007ce..000000000 --- a/demo/data/test/raw/displacement.gml +++ /dev/null @@ -1,173 +0,0 @@ - - - - - 00 - 13-13 - - - - - - 1,-3 1,-2 - Road - CLOCKWISE - - - 1,-2 2,-1 - Road - CLOCKWISE - - - 2,-1 3,-1 - Road - CLOCKWISE - - - 3,-1 4,-2 - Road - CLOCKWISE - - - 4,-2 4,-3 - Road - CLOCKWISE - - - 4,-3 3,-4 - Road - CLOCKWISE - - - 3,-4 2,-4 - Road - CLOCKWISE - - - 2,-4 1,-3 - Road - CLOCKWISE - - - - 5,-3 6,-4 - Road - ANTICLOCKWISE - - - 6,-4 7,-4 - Road - ANTICLOCKWISE - - - 7,-4 8,-3 - Road - ANTICLOCKWISE - - - 8,-3 8,-2 - Road - ANTICLOCKWISE - - - 8,-2 7,-1 - Road - ANTICLOCKWISE - - - 7,-1 6,-1 - Road - ANTICLOCKWISE - - - 6,-1 5,-2 - Road - ANTICLOCKWISE - - - 5,-2 5,-3 - Road - ANTICLOCKWISE - - - - - 1,-6.5 2,-5.5 3,-5 4,-5 5,-5.5 6,-6.5 - - Long Road Name To Go Around The Whole Curve! - CURVE - - - - - 1,-7 2,-8 3,-8.5 4,-8.5 5,-8 6,-7 - - Long Road Name To Go Around The Whole Curve! - CURVE - - - - - 3,-8.5 2,-9.5 1.5,-10.5 1.5,-11.5 2,-12.5 3,-13.5 - - Long Road Name To Go Around The Whole Curve! - VERTCURVE - - - - 4,-8.5 5,-9.5 5.5,-10.5 5.5,-11.5 5,-12.5 4,-13.5 - - Long Road Name To Go Around The Whole Curve! - VERTCURVE - - - - - 9.2,-4 9,-3 10,-3 10.2,-4 - - Road Name - PARALLELOGRAM - - - - 9,-2 9.2,-1 10.2,-1 10,-2 - - Road Name - PARALLELOGRAM - - - - - 11,-1 11,-2 12,-2 - - Road Name - CORNER - - - - 11,-4 12,-4 12,-3 - - Road Name - CORNER - - - - 12.5,-1 13.5,-1 13.5,-2 - - Road Name - CORNER - - - - 12.5,-4 12.5,-3 13.5,-3 - - Road Name - CORNER - - - - diff --git a/demo/data/test/raw/displacement.xsd b/demo/data/test/raw/displacement.xsd deleted file mode 100644 index 66f9b90f0..000000000 --- a/demo/data/test/raw/displacement.xsd +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/demo/data/test/raw/overlap.gfs b/demo/data/test/raw/overlap.gfs deleted file mode 100644 index 67e8c818e..000000000 --- a/demo/data/test/raw/overlap.gfs +++ /dev/null @@ -1,25 +0,0 @@ - - - overlap - overlap - - 1 - 1.00000 - 2.00000 - 1.00000 - 5.00000 - - - NAME - NAME - String - 0 - - - CLASS - CLASS - String - 0 - - - diff --git a/demo/data/test/raw/overlap.gml b/demo/data/test/raw/overlap.gml deleted file mode 100644 index 5fe0e85df..000000000 --- a/demo/data/test/raw/overlap.gml +++ /dev/null @@ -1,127 +0,0 @@ - - - - - 00 - 13-13 - - - - - - 1,-1 1,-10 - Road Name - NETWORK - - - 1,-3 7,-3 - Road Name - NETWORK - - - 7,-3 7,-7 - Road Name - NETWORK - - - 3,-2 3,-8 - Road Name - NETWORK - - - 7,-7 1,-7 - Road Name - NETWORK - - - 5,-2 5,-8 - Road Name - NETWORK - - - - 8,-3 12,-3 - Road Name - CROSS - - - 10,-1 10,-5 - Road Name - CROSS - - - - - 10,-9 13,-9 13,-11 11,-11 11,-8 - - Road Name - SELFOVERLAP - - - - 4,-9 4,-13 - Road Name - NETWORK2 - - - 8,-9 8,-13 - Road Name - NETWORK2 - - - 1,-11 9,-11 - Road Name - NETWORK2 - - - 2,-9 2,-13 - Road Name - NETWORK2 - - - 6,-9 6,-13 - Road Name - NETWORK2 - - - - - 8.8,-6 8.8,-8 - Long Road Name - BENDOVER - - - 8,-6 8,-7 10,-7 10,-8 - Long Road Name - BENDUNDER - - - - 12.2,-6 12.2,-8 - Long Road Name - BENDOVER - - - 11,-6 11,-7 13,-7 13,-8 - Long Road Name - BENDUNDER - - - - 12.2,-3.5 12.2,-5.5 - Long Road Name - BENDOVER - - - 11,-4.5 13,-4.5 - Long Road Name - BENDUNDER - - - - - diff --git a/demo/data/test/raw/overlap.xsd b/demo/data/test/raw/overlap.xsd deleted file mode 100644 index 1a7cf35a2..000000000 --- a/demo/data/test/raw/overlap.xsd +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/demo/data/test/raw/textspacing.gfs b/demo/data/test/raw/textspacing.gfs deleted file mode 100644 index c589181eb..000000000 --- a/demo/data/test/raw/textspacing.gfs +++ /dev/null @@ -1,25 +0,0 @@ - - - textspacing - textspacing - - 1 - 1.00000 - 2.00000 - 1.00000 - 5.00000 - - - NAME - NAME - String - 0 - - - CLASS - CLASS - String - 0 - - - diff --git a/demo/data/test/raw/textspacing.gml b/demo/data/test/raw/textspacing.gml deleted file mode 100644 index 62fff2415..000000000 --- a/demo/data/test/raw/textspacing.gml +++ /dev/null @@ -1,117 +0,0 @@ - - - - - 00 - 13-13 - - - - - - 2,-1 1,-3 2,-5 - Road Name - TRIANGLE - - - 3,-1 4,-3 3,-5 - Road Name - TRIANGLE - - - - 1,-7 2,-7 - Road Name - STRAIGHT - - - 1,-8 3,-8 - Road Name - STRAIGHT - - - 1,-9 4,-9 - Road Name - STRAIGHT - - - 1,-10 5,-10 - Road Name - STRAIGHT - - - - 1,-11 7,-11 - Road Name - STRAIGHT - - - - 1,-12 13,-12 - - Road Name - STRAIGHT - - - - 1,-13 2,-13 5,-13 10,-13 13,-13 - - Road Name - STRAIGHT - - - - 6,-1 5,-3 5,-5 - Road Name - BEND - - - 7,-1 8,-3 8,-5 - Road Name - BEND - - - 5,-6 5,-8 6,-10 - Road Name - BEND - - - 8,-6 8,-8 7,-10 - Road Name - BEND - - - - 10.055915,-1.00031738281 10.6649858,-1.077712483 11.274056,-1.26950068 11.77921,-1.55298308 12.191993,-1.92815928 12.51529,-2.369132 12.746218,-2.8329032 12.884774,-3.2968745 12.930959,-3.875339 - Road Name - CURVE - - - 10.0555,-8.875339 10.6645708,-8.7979439 11.273641,-8.6061557 11.778795,-8.3226733 12.191578,-7.9474971 12.514875,-7.5065244 12.745803,-7.0427532 12.884359,-6.5787819 12.930544,-6.0003174 - Road Name - CURVE - - - - -9.055915,-2.00031738281 9.6649858,-2.077712483 10.274056,-2.26950068 10.77921,-2.55298308 11.191993,-2.92815928 11.51529,-3.369132 11.746218,-3.8329032 11.884774,-4.2968745 11.930959,-4.875339 -11.930544,-5.0003174 11.884359,-5.5787819 11.745803,-6.0427532 11.514875,-6.5065244 11.191578,-6.9474971 10.778795,-7.3226733 10.273641,-7.6061557 9.6645708,-7.7979439 9.0555,-7.875339 - - Road Name - CURVE - - - - -9.0435048,-10.5550195 9.480786,-10.2191668 9.963148,-10.0731439 10.540222,-10.2495527 10.968444,-10.525815 11.419238,-10.8336443 12.01882,-10.9565825 12.559787,-10.7996079 12.956495,-10.4089966 - - Road Name - SQUIGGLE - - - - diff --git a/demo/data/test/raw/textspacing.xsd b/demo/data/test/raw/textspacing.xsd deleted file mode 100644 index 2e6a4d755..000000000 --- a/demo/data/test/raw/textspacing.xsd +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/demo/data/test/readme.txt b/demo/data/test/readme.txt deleted file mode 100644 index c1dc62efd..000000000 --- a/demo/data/test/readme.txt +++ /dev/null @@ -1,3 +0,0 @@ -These files are for testing various rendering parts of mapnik, they have been created by hand. -The raw files are in the raw/ folder (they were created with inkscape to assist!) -Run the regenerate.sh script to regenerate the shape files from the gml files, this requires ogr2ogr to run. diff --git a/demo/data/test/regenerate.sh b/demo/data/test/regenerate.sh deleted file mode 100755 index de1b5babe..000000000 --- a/demo/data/test/regenerate.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh -rm -f textspacing.shp textspacing.shx textspacing.dbf -ogr2ogr -f "ESRI Shapefile" textspacing raw/textspacing.gml -mv textspacing/* ./ -rmdir textspacing - -rm -f overlap.shp overlap.shx overlap.dbf -ogr2ogr -f "ESRI Shapefile" overlap raw/overlap.gml -mv overlap/* ./ -rmdir overlap - -rm -f displacement.shp displacement.shx displacement.dbf -ogr2ogr -f "ESRI Shapefile" displacement raw/displacement.gml -mv displacement/* ./ -rmdir displacement - -rm -f charplacement.shp charplacement.shx charplacement.dbf -ogr2ogr -f "ESRI Shapefile" charplacement raw/charplacement.gml -mv charplacement/* ./ -rmdir charplacement diff --git a/demo/data/test/charplacement.dbf b/tests/data/shp/charplacement.dbf similarity index 100% rename from demo/data/test/charplacement.dbf rename to tests/data/shp/charplacement.dbf diff --git a/demo/data/test/charplacement.shp b/tests/data/shp/charplacement.shp similarity index 100% rename from demo/data/test/charplacement.shp rename to tests/data/shp/charplacement.shp diff --git a/demo/data/test/charplacement.shx b/tests/data/shp/charplacement.shx similarity index 100% rename from demo/data/test/charplacement.shx rename to tests/data/shp/charplacement.shx diff --git a/demo/data/test/displacement.dbf b/tests/data/shp/displacement.dbf similarity index 100% rename from demo/data/test/displacement.dbf rename to tests/data/shp/displacement.dbf diff --git a/demo/data/test/displacement.shp b/tests/data/shp/displacement.shp similarity index 100% rename from demo/data/test/displacement.shp rename to tests/data/shp/displacement.shp diff --git a/demo/data/test/displacement.shx b/tests/data/shp/displacement.shx similarity index 100% rename from demo/data/test/displacement.shx rename to tests/data/shp/displacement.shx diff --git a/demo/data/test/overlap.dbf b/tests/data/shp/overlap.dbf similarity index 100% rename from demo/data/test/overlap.dbf rename to tests/data/shp/overlap.dbf diff --git a/demo/data/test/overlap.shp b/tests/data/shp/overlap.shp similarity index 100% rename from demo/data/test/overlap.shp rename to tests/data/shp/overlap.shp diff --git a/demo/data/test/overlap.shx b/tests/data/shp/overlap.shx similarity index 100% rename from demo/data/test/overlap.shx rename to tests/data/shp/overlap.shx diff --git a/demo/data/test/textspacing.dbf b/tests/data/shp/textspacing.dbf similarity index 100% rename from demo/data/test/textspacing.dbf rename to tests/data/shp/textspacing.dbf diff --git a/demo/data/test/textspacing.shp b/tests/data/shp/textspacing.shp similarity index 100% rename from demo/data/test/textspacing.shp rename to tests/data/shp/textspacing.shp diff --git a/demo/data/test/textspacing.shx b/tests/data/shp/textspacing.shx similarity index 100% rename from demo/data/test/textspacing.shx rename to tests/data/shp/textspacing.shx diff --git a/tests/python_tests/images/support/char_placement.png b/tests/python_tests/images/support/char_placement.png new file mode 100644 index 0000000000000000000000000000000000000000..7abcb6f20aa587d19d5bc59d6505c404f19df3a0 GIT binary patch literal 71821 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%0Dcu%;Jm=WkFm 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 L)z4*}Q$iB});Hf{ literal 0 HcmV?d00001 diff --git a/tests/python_tests/images/support/displacement.png b/tests/python_tests/images/support/displacement.png new file mode 100644 index 0000000000000000000000000000000000000000..644aeb6a5cc41c4b0dd1c1b433bafa6ed8a9c1af GIT binary patch literal 42357 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.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%0Aj8xtTs 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 literal 0 HcmV?d00001 diff --git a/tests/python_tests/images/support/overlap.png b/tests/python_tests/images/support/overlap.png new file mode 100644 index 0000000000000000000000000000000000000000..56abfbaa43c4db8058cd3f0868419452651348a1 GIT binary patch literal 49517 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 Po_H#XnlCEk%|ibN8rV2+ literal 0 HcmV?d00001 From d5c9fea7b642e3a61c71d430e322e3185bbb38fd Mon Sep 17 00:00:00 2001 From: artemp Date: Thu, 16 May 2013 20:31:28 +0100 Subject: [PATCH 017/110] + remove redundant work-around --- bindings/python/mapnik_markers_symbolizer.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/bindings/python/mapnik_markers_symbolizer.cpp b/bindings/python/mapnik_markers_symbolizer.cpp index cfe06efce..200c44959 100644 --- a/bindings/python/mapnik_markers_symbolizer.cpp +++ b/bindings/python/mapnik_markers_symbolizer.cpp @@ -72,16 +72,6 @@ void set_marker_type(mapnik::markers_symbolizer & symbolizer, std::string const& } - -// https://github.com/mapnik/mapnik/issues/1367 -PyObject* get_fill_opacity_impl(markers_symbolizer & sym) -{ - boost::optional fill_opacity = sym.get_fill_opacity(); - if (fill_opacity) - return ::PyFloat_FromDouble(*fill_opacity); - Py_RETURN_NONE; -} - void export_markers_symbolizer() { using namespace boost::python; From b6a937e93db57c4fec9190ae6d30ff6476905927 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 16 May 2013 12:47:36 -0700 Subject: [PATCH 018/110] iwyu --- bindings/python/python_optional.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bindings/python/python_optional.hpp b/bindings/python/python_optional.hpp index 4af5db7e5..348006949 100644 --- a/bindings/python/python_optional.hpp +++ b/bindings/python/python_optional.hpp @@ -23,6 +23,8 @@ #include #include +#include + // boost::optional to/from converter from John Wiegley template From 71870a47b5e7c32e2aa99e2296fdaaf6ec6d6db1 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 16 May 2013 13:17:48 -0700 Subject: [PATCH 019/110] renable lines-shield test and always generate missing reference images - closes #1696 --- ...nes-shield-200-200-1.0-grid-reference.json | 66 ++++++ ...nes-shield-400-400-1.0-grid-reference.json | 118 ++++++++++ ...nes-shield-600-600-1.0-grid-reference.json | 168 ++++++++++++++ ...nes-shield-800-800-1.0-grid-reference.json | 218 ++++++++++++++++++ ...lines-shield-200-200-1.0-agg-reference.png | Bin 0 -> 2382 bytes ...lines-shield-200-200-2.0-agg-reference.png | Bin 0 -> 2992 bytes ...lines-shield-400-400-1.0-agg-reference.png | Bin 0 -> 5741 bytes ...lines-shield-400-400-2.0-agg-reference.png | Bin 0 -> 5463 bytes ...lines-shield-600-600-1.0-agg-reference.png | Bin 0 -> 7036 bytes ...lines-shield-600-600-2.0-agg-reference.png | Bin 0 -> 7853 bytes ...lines-shield-800-800-1.0-agg-reference.png | Bin 0 -> 11368 bytes ...lines-shield-800-800-2.0-agg-reference.png | Bin 0 -> 12371 bytes tests/visual_tests/test.py | 23 +- 13 files changed, 579 insertions(+), 14 deletions(-) create mode 100644 tests/visual_tests/grids/lines-shield-200-200-1.0-grid-reference.json create mode 100644 tests/visual_tests/grids/lines-shield-400-400-1.0-grid-reference.json create mode 100644 tests/visual_tests/grids/lines-shield-600-600-1.0-grid-reference.json create mode 100644 tests/visual_tests/grids/lines-shield-800-800-1.0-grid-reference.json create mode 100644 tests/visual_tests/images/lines-shield-200-200-1.0-agg-reference.png create mode 100644 tests/visual_tests/images/lines-shield-200-200-2.0-agg-reference.png create mode 100644 tests/visual_tests/images/lines-shield-400-400-1.0-agg-reference.png create mode 100644 tests/visual_tests/images/lines-shield-400-400-2.0-agg-reference.png create mode 100644 tests/visual_tests/images/lines-shield-600-600-1.0-agg-reference.png create mode 100644 tests/visual_tests/images/lines-shield-600-600-2.0-agg-reference.png create mode 100644 tests/visual_tests/images/lines-shield-800-800-1.0-agg-reference.png create mode 100644 tests/visual_tests/images/lines-shield-800-800-2.0-agg-reference.png diff --git a/tests/visual_tests/grids/lines-shield-200-200-1.0-grid-reference.json b/tests/visual_tests/grids/lines-shield-200-200-1.0-grid-reference.json new file mode 100644 index 000000000..cd78fedba --- /dev/null +++ b/tests/visual_tests/grids/lines-shield-200-200-1.0-grid-reference.json @@ -0,0 +1,66 @@ +{ + "keys": [ + "", + "212", + "210", + "208", + "132", + "206", + "200", + "202", + "204" + ], + "data": {}, + "grid": [ + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " !! !!! !! !!! !! !!! ", + " !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ", + " !! !! !! !! !! !! ", + " ! ! ! ", + " ", + " ### ", + " ############################################# ", + " ## ", + " # $ $ ", + " %% $$ $$ $$ $$ $$ ", + " %%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ", + " %% $ $$ $ $$ $ ", + " ", + " ", + " ", + " && ", + " ''' &&&&&&&&&& ( (( ", + " '' ' && (( ( ( ", + " ' '' (((( ( ", + " ' ''''' (( (( ", + " ''' ''' ( ( ( ", + " ' '' )) ((( ", + " ' )))))))))) ", + " )) ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " " + ] +} \ No newline at end of file diff --git a/tests/visual_tests/grids/lines-shield-400-400-1.0-grid-reference.json b/tests/visual_tests/grids/lines-shield-400-400-1.0-grid-reference.json new file mode 100644 index 000000000..56dc7a8e9 --- /dev/null +++ b/tests/visual_tests/grids/lines-shield-400-400-1.0-grid-reference.json @@ -0,0 +1,118 @@ +{ + "keys": [ + "", + "216", + "212", + "210", + "132", + "208", + "240", + "206", + "202", + "200", + "204" + ], + "data": {}, + "grid": [ + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ! ! ! ! ! ! ", + " !! !! !! !! !! !! !! !! !! !! !! !! ", + " !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ", + " !!! !! !!! !! !!! !! !!! !! !!! !! !!! !! ", + " ", + " ", + " ", + " ", + " # # # # # # ", + " # ## # ## # ## # ## # ## # ## ", + " # ## # ## # ## # ## # ## # ## ", + " ### # ### # ### # ### # ### # ### # ", + " ", + " ", + " ", + " ", + " ", + " ", + " $ $ $ $ $ ", + " $$ $$ $$ $ $ $$ $$ $$ $ $ $ ", + " $$ $$ $$ $ $ $ $$ $$ $ $ $ ", + " $$$ $ $ $ $ $$$ $$$ $ $ $ $$$ $$$ ", + " ", + " ", + " ", + " % & & & & & & ", + " %% && && && && && && && && && && && ", + " %%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ", + " %%% && & && & && & && & && & && ", + " ", + " ' ' ' ' ' ' ", + " ' '' ' '' ' '' ' '' ' '' ' '' ", + " '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ", + " ' '' ' '' ' '' ' '' ' '' ' '' ", + " ' ' ' ' ' ' ", + " ", + " ", + " ", + " (( ( ( )) )))) ) ", + " *** *** ((((((((((((((((((( )))) ))) ", + " ***** *** (( ( )) ) ", + " ** ** ) ))) ", + " ** *** ) ) ", + " ) ", + " * * ", + " * * ) ) ", + " * )) ", + " * * ) )) ", + " ** ** )))) )))) ", + " **** **** + )))) )))) ", + " * * * ** ++ + ) ", + " * * +++++++++++++++++++ ", + " ++ + ", + " + ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " " + ] +} \ No newline at end of file diff --git a/tests/visual_tests/grids/lines-shield-600-600-1.0-grid-reference.json b/tests/visual_tests/grids/lines-shield-600-600-1.0-grid-reference.json new file mode 100644 index 000000000..db3b627ed --- /dev/null +++ b/tests/visual_tests/grids/lines-shield-600-600-1.0-grid-reference.json @@ -0,0 +1,168 @@ +{ + "keys": [ + "", + "216", + "212", + "210", + "208", + "132", + "240", + "202", + "200", + "206", + "204" + ], + "data": {}, + "grid": [ + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ! !!! ! !!! ! !!! ! !!! ! !!! ! !!! ! !!! ! !!! ! !!! ", + " ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ", + " !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! ", + " ! ! ! ! ! ! ! ! ! ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " # # # # # # # # # ", + " # ## # ## # ## # ## # ## # ## # ## # ## # ## ", + " ######################################################################################################################################## ", + " # ## # ## # ## # ## # ## # ## # ## # ## # ## ", + " # # # # # # # # # ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " $ $ $ $ $ $ $ $ $ ", + " $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ ", + " $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ", + " $$ $$$ $$ $$$ $$ $$$ $$ $$$ $$ $$$ $$ $$$ $$ $$$ $$ $$$ $$ $$$ ", + " $ $ $ $ $ $ $ $ $ ", + " ", + " ", + " ", + " ", + " ", + " ", + " % % % % % % % % ", + " && %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% ", + " &&%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ", + " && % %% % %% % %% % %% % %% % %% % %% % %% % ", + " ", + " ", + " ", + " ", + " '' ' ' '' ' ' '' ' ' '' ' ' '' ' ' '' ' ' '' ' ' '' ' ' '' ' ' ", + " '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' ", + " '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ", + " ''' ' ' ''' ' ' ''' ' ' ''' ' ' ''' ' ' ''' ' ' ''' ' ' ''' ' ' ''' ' ' ", + " ", + " ", + " ", + " ", + " ", + " ", + " ( ( (( ( ", + " ) ** * *** (( ((( ", + " )) ) )) )) **************************** (((( (( ", + " )))) )))) ** ** ** ((( ( ", + " ) ))) ))) * * ( ( ", + " ) ) ", + " ) (( ( ", + " ))) ) (((( (( ", + " ) ) ( ( (( ", + " ) )))) ( (( ", + " ))) )) ( ", + " ) ) ( ", + " ) ( ( ", + " ) ((( ", + " ) ) ( (( ", + " )) ) ) ( (((( ", + " ))))) ) ( ( ( ", + " ))) ) ( ( ( (( ", + " ) ) ))) ) + + ((( ", + " )) ) ++ ++ ++ (( ", + " )))) ++++++++++++++++++++++++++++ ", + " ) ) ++ ++ +++ ", + " + + ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " " + ] +} \ No newline at end of file diff --git a/tests/visual_tests/grids/lines-shield-800-800-1.0-grid-reference.json b/tests/visual_tests/grids/lines-shield-800-800-1.0-grid-reference.json new file mode 100644 index 000000000..134943ab2 --- /dev/null +++ b/tests/visual_tests/grids/lines-shield-800-800-1.0-grid-reference.json @@ -0,0 +1,218 @@ +{ + "keys": [ + "", + "216", + "212", + "210", + "132", + "208", + "240", + "202", + "200", + "206", + "204" + ], + "data": {}, + "grid": [ + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ! ! ! ! ! ! ! ! ! ! ! ! ", + " !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! !! ", + " !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ", + " !! ! ! !! ! ! !! ! ! !! ! ! !! ! ! !! ! ! !! ! ! !! ! ! !! ! ! !! ! ! !! ! ! !! ! ! ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " # ### # ### # ### # ### # ### # ### # ### # ### # ### # ### # ### # ### ", + " ## # ## # ## # ## # ## # ## # ## # ## # ## # ## # ## # ## # ", + " ## # ## # ## # ## # ## # ## # ## # ## # ## # ## # ## # ## # ", + " # # # # # # # # # # # # ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " $$ $$$ $$$ $ $ $ $$ $$ $$$ $$$ $ $ $ $$ $$ $$$ $$$ $ $ $ $$ $$ $$$ $$$ $ $$ ", + " $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ", + " $$ $$ $ $$ $$ $$ $$ $$ $ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ ", + " $ $ $ $ $ $ $ $ $ $ $ $ ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " % & & & & & & & & & & & & ", + " %% && && && && && && && && && && && && && && && && && && && && && && && ", + " %%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ", + " %%% && & && & && & && & && & && & && & && & && & && & && & && ", + " ", + " ", + " ", + " ", + " ", + " ", + " ' ' ' ' ' ' ' ' ' ' ' ' ", + " ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ", + " '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ", + " '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ", + " ' ' ' ' ' ' ' ' ' ' ' ' ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ( ", + " (((( ( (( ", + " ) *** * * ** * * * ((( ((((( ", + " ) ) ) ) * * * * ** ( ( ((( ", + " )))) )))) ** ** ** ** ** ( ( ( ", + " )) ))) )))) * * (( ( ", + " ) )) ( ((( ", + " ))) (( ((((( ", + " ) )) ( ( ", + " )) )) ( ( ", + " ) )) ( ", + " ) ( ( ", + " ) ", + " (( (( ", + " ) ) (((( (( ", + " ) (( (((( ", + " )))) )) ( (( ", + " )) )))) (( ", + " )) ( ( ", + " ) ( ( ", + " ) ) ( ( ( ( ", + " ) (((( (( ", + " ))) ) ((( ((((( ", + " )) ))) (( ( ( (( ", + " ))) )))) (((((( ( ", + " ) ) )) )) + + + (((( ", + " ))) ) ++ ++ ++ ++ ++ (( ", + " )))) ++++++++++++++++++++++++++++++++++++++ ", + " ) +++ ++ ++ + ++ ", + " + + + ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " " + ] +} \ No newline at end of file diff --git a/tests/visual_tests/images/lines-shield-200-200-1.0-agg-reference.png b/tests/visual_tests/images/lines-shield-200-200-1.0-agg-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..b1201330ac04cd80eb517cdb4454da6498ae4904 GIT binary patch literal 2382 zcmbtWi#ybL7dHtRm(6A5ZvAMO#-%1yj4;hi?)Tg_m%M5bVT?=B4!c;)jxo6uO~O0U zL=$7EC`|53$tcAX8HqKqRF+)6uYLc8JqjTtY%Z z!O0Qty~F+g_#VlfSe_G7FChUVd%F4(U=k9$VK6uhCM6{Whr{(@Fa-q#B_$gwv6@72R-Xh0BzL?Tfrl&Ps{n4&fYgK;x5O5eAy5eBox z+Tw6HXJ=<0AD_#1cH6KW1Ku#sJ($Qs)z#Hx zGGMdW6bb+oN>&!&a5#f3R(?KcYH9+-#iga7wY3$Ll>rv3ySux-9)SA#{{DV07Yq#z zf&2Fb0>OuNA$annyBkbQOn||`XU_l_9u|p2;x`LIApk<*^fVBQ#b9=JVFB!90D#X% zE^`tRG9^xU2j5iY=j&Ucf}`r4J~mUgNX7@>_at80^+d5REMqD`s468B)0wLBwpaQD zx&J|w#?~iuH=TwSrwNDqKVMripXn;PwLZeTSgvX~sqEQo6YmqR6#4HVBr;-ntBzWE z%=C-Aj50 zl5SHs)4$Ig=&Y6_LN%_k9cgnEja0Al{;D_YUVC1SORCr8rDFNQ2dTa`UitmSskyZk zrK{t4npEw4pF!<%<=JRtveq01Eb{=yh*ey6S>dif z*XH91L?B=xX=^{b#iO_n*|bC4(90jAPXGnK2dN%Y z)r%_#y4-lypX1Imo?f0hX%SoNDAz_xs_rQZ#&#!}m~HQLLfy$Xz8*9vj*EJg75p&B z>)eQ=Ow35+IF4YwGeE3RZ(#Jt9BAqM-V==R9IE|qRQs-rjHP7c_)0>^%mhnR>lq&~ zbCiH*jk23QC7d?g{|p7ug#}CQM{Rk;9(oZXa3p0@H)`3n^zndq?h9-nw$X1afl)jh zINLdB^3^;7Zqv2YG@BGlWp+y&jnu=lUABGZPef*`aJOCbMjK)9i5LnSj5Q>!A7VY&4~~sE4mWJ#c(1NtpZVA0|~g zHp|MO0HwEGrfZfdr;l65u>ECK%XyDNa_&veS*Z<3_xke~jpFy11xGYAkjt!iy`?sp z`Q_6so%4V4RjO(9nskmRY|-sz>h1Nd7V|r~+F4OQ*!wV=3n5ISMNO=JyK%=s1b@r> zT=GJw#+ffuSM|?0Z)~Wpge&E3yXCR>xMQv`Tq{G`5>hl5&5a?ntl#}7vX*1;rs$GF z`x{Me(!c-%wf|I_rgi(+_4Q#*gdP6exK`3t-aDN>NYv_3*>hoap-m7odTTV5a`F0d z)F0VttdAeK`{wPmjw-i>GWF(UGE18-fAQJdZY0~5GG>Wo@T;S9WTM9B zJ->U71)@chx#AU5+*-VhEi(IE_|(GBIGH=Z5uTHR^ogL*hcksv-ow0M-y`AH4Prs? zr@JuH!3+qxTKYQRg&n`tNo{Qo~WfY%2|DXd_IQxO2I4%7m@y- z@B>_$-xWq@>o2LgX!>MfyeBbb-m%tsM17*r!XF=C}b@k?6D>f8KIzw%d4-Oxq@xw67lpklbeTcuK3}fznEFm z`kdywNsH0nu)Vv~1=Y3|3yRTkRqG0ru!Y~>!qg`B%Dv>jdDQ}6zPjeNP`93mj!X(! zd|At!ow`390J|9(ZhY=6p?}3@gZ^eJJi#`@WHbQF>9g$!FC+!&>*+A(LwTu?R-JM7 z-Ky-E0UQnC9;~w}^STEmDD{Yl3r25~#zVQD|224=$hRqR_XXvuW(GDb?$HWuXj)X! zvL-$FuSG zkKDEWU2g^b?o?w`?Uia1{ijbIK2HCIVElrlS=#YtFx<(NDLwgr4$8lQh*JKC;Z^o2 SD0=5ll5irp<2ksnU;hUP{Cgw- literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/lines-shield-200-200-2.0-agg-reference.png b/tests/visual_tests/images/lines-shield-200-200-2.0-agg-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..79245d9bc66203552bdeb6fa0aa599661a018113 GIT binary patch literal 2992 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⪚XV2rUH(6AK z83F_*1Y(bm>%6#?32|L^IM0+d=a`vAU&~i{m@?u(PJg@nk9vCp@IuBX4DL=!ZlQRR zwe+PJYF5PE-hVuq4K3>Yb8v7j`WSACW~Kg2WqB&V$I7SlU%pvj?5ZPFSB>9D>s~08 z*43(Sx&oLV9DHy6%v-XAXNrr#sU|;n0TKVPO>-(xvqUjz!We{Pft&EfeVY= zsvUZNwJhk4xtRAx zW@;Z9Zpq}LD8|mJplE7jCx7#$MKtC{y}En<0{3-UCci`txhfm>aM}4A<%@STT(evv?N7}DMNi&-$^BN7a6xk9 z^VeHr7cHY*fP#0jO4hUcW2cZ-tjITSOW#sOD0t1f*Fi?5fIBJyV^Q`t<=6l?47qeD zrFGE-64_Feawe)t_>JXHt5f%wo?<1}Jrp;e*$q1W`xsU;3E`X!aun(xND@ae3hf-5 z*`}17<^*nw#lH?ZfksMQc^{y;WndU&JIdieJ1ua{1cg)l7O3LL!ysL z@S6|s-kqKsA$g7f`%l3-eKuWM!2eaMtB}3g*N5Q+q27$-*HUiW=4bB$PKW<{C0{=a z7>~d3+1K^$Lqq+i+uOeLcU<^S8&LQ@hJ1lH?;D9-PF=-Qem4PbAtjW@$Of)*Ow3`W zuI)0#p=takw%^SgpjDQgzM|cse!e#54JjAB^a14{ROkCpK+cN+m`wYy_$0JhxY}o+ zEC>+r3h#{JEf4MtRUiuY?bIffH%r!ki@_HK?ghPJBc{iBldoF|A9~_{+UPv{o@uv> zM2#K{-WV8@S;wRIeR($6jOQbzl52yfh6Sx+vTX6Qcs+`pwKr7XyJ6$tGW;`B9ZsEN z0uvmVz`v3_=|zgF2KT?2*dxh?+Nj6PWl?ng=#(`DNK(rwNtE-!bWfG#i-ITh4lWZ6 zCoCKO{xg}z_}g#OTb4hUp?|)-i)hj6^Iv~XTAX!%H9E*M z{)nu22$3<)#<~?B{<(E8m;OgkfcjFsjRt>3Q18f5gQy4!q6*XSS5*=ZM|aV_(z4Ysp@kH zZTDyPQmZvR=#Y!8-u>-YX5ze5_BYEWhzl6K`b@2tSw}e;`F{_IYM@_6^$dE< ztQ{4Bj?Q_JYqHvuPTCue&esPvvHCAPYZaE8dbBfMjd(Gn!#gJSH^W-m@<26a#2`A8 zqiG1sUcFo=RW5W-ZDd6%4LZ7}1=mtATdp0StbVRG65sD9sXK2(G35+r5e!|gZ!xZkn~$nl|ybL z@b-~-`*vkBjP>tQ_!*17~DI*i|jkTm1LD~3AIT7CR^|dyO+|R z9IkCSH1;fmgciQzK0@>ON@GfuPMflm6<65Uz&RwEjz>2)wK)dMl840SjTh}ITmQO} zPrsrPYEJh5+4bmmWMoeLX91MNJkn-wxs8TxyW^TKY8^Ch zXMNxeXGuLw&HpQMm0dp`VqJMex^P1WGNt!4KmJ7%R70*b!6ECOXBq3WQkf^}UK`^^ zm@}!hnp@+N`wJx??_|NFR^y}a>3BZz)!n8@5yB0auY4$q0-H`uanh1&!(RGZe~m7( zy1@0py6TYpa3yPfY^%j^(U;5HoACwHb?#F0U&=-Kx064t{iJ%O*pW`0w45w5=Q~N+ zJ&FlnID~zP;9t1p7ZSm$VfGk4YU@kaYFo=c;p$$iXXD0ZNi&;ytQ+yO^qgAIPVNDO z7(PVpbhL0=`A3IT3-de=h;+ez_a|&+fi4bx7k=-P&hcXJQf&h+Be`wq?%|8?H52i!)^(BnAdVGoXn-g&<&b4KsIQC<50kobO~NR;HP9EJ zb$B9D{%)Rrq)A2ipqjDaQU44Qx4gf(o162C-}UCuelXjyYb^4SCg{SEQe2N!~67Fl;b95 ztJil_-0KY|>#xK81FhhVWMSlTxhJtA;cebLEK5scS*=ExKhD|$ zUWvKHY#Tk&=1`1Lu&a%r7D~XA00tnfeuKrwAP()Mf!pI;brRDhT7=|*S$%KEo#AHW zD7e3^WFGkx zSR*22axG71T-HN_Ul zrE=UPer5Y9=TLm4>ODODk**lIc)!=?maV#M-C&Q&s6efBbSzjmMn+;Y;vL7OQnd>uLXC5`i@Kk3ngRd~pyjx|8 ztoLV-NX`REKV$Z@y%X|`NDucR9hLM*EUR3rO-y$*mvdgoUV9KYhcl_@xy5%^nrz}i zcfksGl~2Y($vHD!uHu{!r_b|T(YB~0!HVaJIYMaF@qsiJ!Ic+7#gns*M_Ho$#4V+r zh+ldEcgzu$46zvxt~cFzD{r5ZpJe}M)qkX<-~LviZjNgq?{xUU*8owhMLIUq> zpm?aLQppu=`VgQ$&tp9>vH$HDatwm-SkHt^{v_GL-`nFbQZsLlTpF*vsG$NbtitM$ z4KtgNUM2(qr^Q}hD)@f+Y816ga)PFKE?>!qy1Src*?9ZV(|scIGwhO7q+4m00Q zfg+?oF^BQI>Q>R%hryS4nlVYC20jZPuHnb|DYfsd^%^}qBVLgY2;e_r)^X-FDZCuq zO&G*KLz{&Bi}Parh_PqXCQl9g|6s85pT70KkFQQxq7d!2tJ#@Rr&CS+>u6L3(kbd+ Di&m^@ literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/lines-shield-600-600-1.0-agg-reference.png b/tests/visual_tests/images/lines-shield-600-600-1.0-agg-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..a8d2ba95eb7d027cb5a7e837345f5029e5601069 GIT binary patch literal 7036 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 D#w9B> literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/lines-shield-600-600-2.0-agg-reference.png b/tests/visual_tests/images/lines-shield-600-600-2.0-agg-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..d1317ba1b716698977b5f3d190f898efe6c92a5c GIT binary patch literal 7853 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 BZ@>Tm literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/lines-shield-800-800-2.0-agg-reference.png b/tests/visual_tests/images/lines-shield-800-800-2.0-agg-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..489f43df6007eeb8932beda5ed77216cd51fbae7 GIT binary patch literal 12371 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 literal 0 HcmV?d00001 diff --git a/tests/visual_tests/test.py b/tests/visual_tests/test.py index d149b8cfc..7ed969245 100755 --- a/tests/visual_tests/test.py +++ b/tests/visual_tests/test.py @@ -43,7 +43,7 @@ files = [ # https://github.com/mapnik/mapnik/issues/1696 # https://github.com/mapnik/mapnik/issues/1521 # fails with clang++ on os x - #{'name': "lines-shield", 'sizes': sizes_few_square,'bbox':default_text_box}, + {'name': "lines-shield", 'sizes': sizes_few_square,'bbox':default_text_box}, {'name': "collision", 'sizes':[(600,400)]}, {'name': "marker-svg-opacity"}, {'name': "marker-multi-policy", 'sizes':[(600,400)]}, @@ -103,11 +103,10 @@ class Reporting: NOT_FOUND = 2 OTHER = 3 REPLACE = 4 - def __init__(self, quiet, generate = False, overwrite_failures = False): + def __init__(self, quiet, overwrite_failures = False): self.quiet = quiet self.passed = 0 self.failed = 0 - self.generate = generate self.overwrite_failures = overwrite_failures self.errors = [ #(type, actual, expected, diff, message) ] @@ -119,7 +118,7 @@ class Reporting: else: print '\x1b[31m✘\x1b[0m (\x1b[34m%u different pixels\x1b[0m)' % diff - if self.generate: + if self.overwrite_failures: self.errors.append((self.REPLACE, actual, expected, diff, None)) contents = open(actual, 'r').read() open(expected, 'wb').write(contents) @@ -139,10 +138,9 @@ class Reporting: if self.quiet: sys.stderr.write('\x1b[33m.\x1b[0m') else: - print '\x1b[33m?\x1b[0m (\x1b[34mReference file not found\x1b[0m)' - if self.generate: - contents = open(actual, 'r').read() - open(expected, 'wb').write(contents) + print '\x1b[33m?\x1b[0m (\x1b[34mReference file not found, creating\x1b[0m)' + contents = open(actual, 'r').read() + open(expected, 'wb').write(contents) def other_error(self, expected, message): self.failed += 1 @@ -152,7 +150,7 @@ class Reporting: else: print '\x1b[31m✘\x1b[0m (\x1b[34m%s\x1b[0m)' % message - def summary(self, generate=False): + def summary(self): if len(self.errors) == 0: print '\nAll %s visual tests passed: \x1b[1;32m✓ \x1b[0m' % self.passed return 0 @@ -161,10 +159,7 @@ class Reporting: if error[0] == self.OTHER: print str(idx+1) + ") \x1b[31mfailure to run test:\x1b[0m %s" % error[2] elif error[0] == self.NOT_FOUND: - if self.generate: - print str(idx+1) + ") Generating reference image: '%s'" % error[2] - else: - print str(idx+1) + ")Could not verify %s: No reference image found!" % error[1] + print str(idx+1) + ") Generating reference image: '%s'" % error[2] continue elif error[0] == self.DIFF: print str(idx+1) + ") \x1b[34m%s different pixels\x1b[0m:\n\t%s (\x1b[31mactual\x1b[0m)\n\t%s (\x1b[32mexpected\x1b[0m)" % (error[3], error[1], error[2]) @@ -289,6 +284,6 @@ if __name__ == "__main__": reporting) mapnik.save_map(m, os.path.join(dirname, 'xml_output', "%s-out.xml" % config['name'])) - sys.exit(reporting.summary(generate=True)) + sys.exit(reporting.summary()) else: print "OSM plugin required" From f665bcb11e0398622cc2e6db8241a374dde9121c Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 16 May 2013 20:25:16 +0000 Subject: [PATCH 020/110] xadd expected cairo outputs for lines-shield test (via ubuntu precise) --- ...lines-shield-200-200-1.0-cairo-reference.png | Bin 0 -> 2155 bytes ...lines-shield-200-200-2.0-cairo-reference.png | Bin 0 -> 2761 bytes ...lines-shield-400-400-1.0-cairo-reference.png | Bin 0 -> 4994 bytes ...lines-shield-400-400-2.0-cairo-reference.png | Bin 0 -> 5134 bytes ...lines-shield-600-600-1.0-cairo-reference.png | Bin 0 -> 6370 bytes ...lines-shield-600-600-2.0-cairo-reference.png | Bin 0 -> 7171 bytes ...lines-shield-800-800-1.0-cairo-reference.png | Bin 0 -> 9452 bytes ...lines-shield-800-800-2.0-cairo-reference.png | Bin 0 -> 10567 bytes 8 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/visual_tests/images/lines-shield-200-200-1.0-cairo-reference.png create mode 100644 tests/visual_tests/images/lines-shield-200-200-2.0-cairo-reference.png create mode 100644 tests/visual_tests/images/lines-shield-400-400-1.0-cairo-reference.png create mode 100644 tests/visual_tests/images/lines-shield-400-400-2.0-cairo-reference.png create mode 100644 tests/visual_tests/images/lines-shield-600-600-1.0-cairo-reference.png create mode 100644 tests/visual_tests/images/lines-shield-600-600-2.0-cairo-reference.png create mode 100644 tests/visual_tests/images/lines-shield-800-800-1.0-cairo-reference.png create mode 100644 tests/visual_tests/images/lines-shield-800-800-2.0-cairo-reference.png diff --git a/tests/visual_tests/images/lines-shield-200-200-1.0-cairo-reference.png b/tests/visual_tests/images/lines-shield-200-200-1.0-cairo-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..1627c6f0e0a4100b17b27c34787f56401744f6f8 GIT binary patch literal 2155 zcmb7Gc{mhW8y^ueRAVM%3Af3Xn9A0bYb0hcWSJ{zFiLanTVsr!Pw37X|j!>$*$xcVx=Gu87T?2OQ4{jfQCTyw?j>K?AWh}L@O$4 zYHFf)X`862>FVkl7#J8E8~ZD1M8FXyCMGBK^iq&W3GW@b;!sYyvmR#pJ~@=KacXQxe?tu07TPsd_GL1Lm9hs(;!dg9DFI?9 z03=*oTv`GWj4Is%5QyxolfA8HeDTV4A3K2~;$L|MxQ)ya#fc;|As-SXTM*G5u~v2`>WjUO}UJ0lCovu35F<8P`Sn*dhUjnHfnpO+{(Nqu0>x+Q;qj1rB~&YZru{W zOAXxYg)cNWyQ1UEI!gUaU+8V}HizxUQMUt!$!EPbj~el>mzv4O92PRZU};(N+oJjY zXERkzQkB@g^_R<_bo<45Ypd*~9yj&MD*2!_#a&%bLK*Z`+|cUW4dQ`;y?E$r8<_j9l(?_5N%3SqJ41*6@8!R#p28e=H(OhzC~YZ&OFmb$eab zw$vFjvmCdHr}@&1QWj?vw!!uDwamQ}FulXTuHiN6H@jYciwvH1`|fCtE=9<4?4&r{ zYoEc^(_gDDI{)u*uygOIRs>#u=|={|@Cos?up7lhrS4$9VRWstYn|n z6r>75L<*ScNpOup)gYFz8EV zW!kBQJ1qD!jBmN{Hz)c`u)ccIiOWY7xD3hmgy{(WA1?@WKKl74q!GVnrn6pBjKU@fGG)!*H*SZ#X zbtk_Ebu=X>G=YDi-1J#vfxHbide1%^7|dz#81XeJ3=;=$xLsC*c-n)C!zca6{Nu8% zb7(eQo*;7?_65H3@5=#$DkPg=Z&Y0fN60;rm~`m@ zU2rS!U~)#K0tDsK4HKKYE~(IwCQhYu9H^P9nb9^Ee(dPrgR{u072r}^v6 z);Lb;yE?!17ZWa9I<;RaS^Isd?l}ph~(80rWT#fBC z!^7UJy79JI?g&1|YWhw8{r9PWbbdw=`*$@D<{Ua8^vX>35g*lGZDK`dY?sWdOA>Iq zs%Iy7bb?GA%x{*b<88d`fLoeQ^%_piF(k#V-Nca#sq~A8u17(_{%K#z@BHp3kPi;Y z@m|p%bv`-9o;3DqCLmIob9wx?7D zzVRdBxTL7aLl3V+z5v7%ncQM~Zbm{akJtZaliw=~Wpd`qgY*UY2EK((IHH#Qs$uie zfrzl?ri5BzDn&G^eoo(l9GJQAe0ifPJPb1z96@;+YcZGoUNq(kZ=CqPFj-YIO3tl3 zg9u|*h5I0M^{9iZYkR$|D&ZmpDF$Yna>SWOQ3=-oD z^6dQ_ATHh&Wgy~dwnxKW#3i>RC*LqJ_dGW;VZNnur1{!i>x6`!D43tV$ng+qW;N+4 z-jwmfI^B!6tVLAF9g14-R9&rR}# F_+Q#I2Au!^ literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/lines-shield-200-200-2.0-cairo-reference.png b/tests/visual_tests/images/lines-shield-200-200-2.0-cairo-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..f21528244fac1e69c35f3ada36327f8773d33b13 GIT binary patch literal 2761 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 literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/lines-shield-400-400-1.0-cairo-reference.png b/tests/visual_tests/images/lines-shield-400-400-1.0-cairo-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..16f7c54343af3a307be6d82be5c6340df4114e41 GIT binary patch literal 4994 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 literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/lines-shield-600-600-2.0-cairo-reference.png b/tests/visual_tests/images/lines-shield-600-600-2.0-cairo-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..d18ce44d241614db0ddbeaa0b4aeb8d29cae5e3f GIT binary patch literal 7171 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 literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/lines-shield-800-800-1.0-cairo-reference.png b/tests/visual_tests/images/lines-shield-800-800-1.0-cairo-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..50cc54c4f94f2d1bbfb4ee35c38f8c89f0cbfef4 GIT binary patch literal 9452 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 literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/lines-shield-800-800-2.0-cairo-reference.png b/tests/visual_tests/images/lines-shield-800-800-2.0-cairo-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..dbec8e0de840fd0ab6217cc7be01df3b2fc39e41 GIT binary patch literal 10567 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 Date: Thu, 16 May 2013 14:24:25 -0700 Subject: [PATCH 021/110] add more box2d multiplication tests --- tests/python_tests/box2d_test.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/tests/python_tests/box2d_test.py b/tests/python_tests/box2d_test.py index ef7a2f95b..8fa9b20b5 100644 --- a/tests/python_tests/box2d_test.py +++ b/tests/python_tests/box2d_test.py @@ -100,8 +100,34 @@ def test_envelope_static_init(): eq_(c.y, 150) def test_envelope_multiplication(): + # no width then no impact of multiplication + a = mapnik.Box2d(100, 100, 100, 100) + a *= 5 + eq_(a.minx,100) + eq_(a.miny,100) + eq_(a.maxx,100) + eq_(a.maxy,100) + + a = mapnik.Box2d(100.0, 100.0, 100.0, 100.0) + a *= 5 + eq_(a.minx,100) + eq_(a.miny,100) + eq_(a.maxx,100) + eq_(a.maxy,100) + + a = mapnik.Box2d(100.0, 100.0, 100.001, 100.001) + a *= 5 + assert_almost_equal(a.minx, 99.9979, places=3) + assert_almost_equal(a.miny, 99.9979, places=3) + assert_almost_equal(a.maxx, 100.0030, places=3) + assert_almost_equal(a.maxy, 100.0030, places=3) + e = mapnik.Box2d(100, 100, 200, 200) e *= 2 + eq_(e.minx,50) + eq_(e.miny,50) + eq_(e.maxx,250) + eq_(e.maxy,250) assert_true(e.contains(50, 50)) assert_true(e.contains(50, 250)) @@ -148,5 +174,4 @@ def test_envelope_clipping(): eq_(e1,e2) if __name__ == "__main__": - setup() [eval(run)() for run in dir() if 'test_' in run] From 392561c5d9fe2a76fda9cf6cb4a93da47a1ad005 Mon Sep 17 00:00:00 2001 From: artemp Date: Fri, 17 May 2013 11:16:11 +0100 Subject: [PATCH 022/110] add to/from specialisation for boost::optional --- bindings/python/mapnik_raster_symbolizer.cpp | 13 +---- bindings/python/python_optional.hpp | 54 +++++++++++++++++++- 2 files changed, 53 insertions(+), 14 deletions(-) diff --git a/bindings/python/mapnik_raster_symbolizer.cpp b/bindings/python/mapnik_raster_symbolizer.cpp index 54f66a554..20a04a3f3 100644 --- a/bindings/python/mapnik_raster_symbolizer.cpp +++ b/bindings/python/mapnik_raster_symbolizer.cpp @@ -28,18 +28,7 @@ #include #include -namespace { -// https://github.com/mapnik/mapnik/issues/1367 -PyObject* get_premultiplied_impl(mapnik::raster_symbolizer & sym) -{ - boost::optional premultiplied = sym.premultiplied(); - if (premultiplied) - return ::PyBool_FromLong(*premultiplied); - Py_RETURN_NONE; -} - -} using mapnik::raster_symbolizer; void export_raster_symbolizer() @@ -132,7 +121,7 @@ void export_raster_symbolizer() ">>> r.mesh_size = 32\n" ) .add_property("premultiplied", - &get_premultiplied_impl, + &raster_symbolizer::premultiplied, &raster_symbolizer::set_premultiplied, "Get/Set premultiplied status of the source image.\n" "Can be used to override what the source data reports (when in error)\n" diff --git a/bindings/python/python_optional.hpp b/bindings/python/python_optional.hpp index 348006949..9bc38d119 100644 --- a/bindings/python/python_optional.hpp +++ b/bindings/python/python_optional.hpp @@ -102,7 +102,7 @@ struct python_optional : public mapnik::noncopyable } }; -// to/from boost::optional +// to/from boost::optional template <> struct python_optional : public mapnik::noncopyable { @@ -130,7 +130,7 @@ struct python_optional : public mapnik::noncopyable boost::python::converter::rvalue_from_python_stage1_data * data) { using namespace boost::python::converter; - void * const storage = ((rvalue_from_python_storage > *) + void * const storage = ((rvalue_from_python_storage > *) data)->storage.bytes; if (source == Py_None) // == None new (storage) boost::optional(); // A Boost uninitialized value @@ -147,6 +147,56 @@ struct python_optional : public mapnik::noncopyable } }; +// to/from boost::optional +template <> +struct python_optional : public mapnik::noncopyable +{ + struct optional_to_python + { + static PyObject * convert(const boost::optional& value) + { + if (value) + { + if (*value) Py_RETURN_TRUE; + else Py_RETURN_FALSE; + } + else return boost::python::detail::none(); + } + }; + struct optional_from_python + { + static void * convertible(PyObject * source) + { + using namespace boost::python::converter; + + if (source == Py_None || PyBool_Check(source)) + return source; + return 0; + } + + static void construct(PyObject * source, + boost::python::converter::rvalue_from_python_stage1_data * data) + { + using namespace boost::python::converter; + void * const storage = ((rvalue_from_python_storage > *) + data)->storage.bytes; + if (source == Py_None) // == None + new (storage) boost::optional(); // A Boost uninitialized value + else + { + new (storage) boost::optional(source == Py_True ? true : false); + } + data->convertible = storage; + } + }; + + explicit python_optional() + { + register_python_conversion, + optional_to_python, optional_from_python>(); + } +}; + // This class works around a feature in boost python. // See http://osdir.com/ml/python.c++/2003-11/msg00158.html From 9686999a2cb04f41e78d660205bf1496c811bc8b Mon Sep 17 00:00:00 2001 From: artemp Date: Fri, 17 May 2013 13:13:18 +0100 Subject: [PATCH 023/110] fix sqlite build --- plugins/input/sqlite/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/input/sqlite/build.py b/plugins/input/sqlite/build.py index e6de86d2c..72fb24a0f 100644 --- a/plugins/input/sqlite/build.py +++ b/plugins/input/sqlite/build.py @@ -52,7 +52,7 @@ if env['PLUGIN_LINKING'] == 'shared': SHLIBSUFFIX='.input', source=plugin_sources, LIBS=libraries, - LINKFLAGS=linkflags) + LINKFLAGS=join(' ').linkflags) # if the plugin links to libmapnik ensure it is built first Depends(TARGET, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) From 5be9640742f6ee427661c71887203ce068b8e4e9 Mon Sep 17 00:00:00 2001 From: artemp Date: Fri, 17 May 2013 16:22:04 +0100 Subject: [PATCH 024/110] + filter_at_point - cache inflated bbox --- include/mapnik/geom_util.hpp | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/include/mapnik/geom_util.hpp b/include/mapnik/geom_util.hpp index 36f030174..350c797ab 100644 --- a/include/mapnik/geom_util.hpp +++ b/include/mapnik/geom_util.hpp @@ -200,10 +200,10 @@ inline bool point_on_path(double x,double y,Iter start,Iter end, double tol) struct filter_in_box { box2d box_; - explicit filter_in_box(const box2d& box) + explicit filter_in_box(box2d const& box) : box_(box) {} - bool pass(const box2d& extent) const + bool pass(box2d const& extent) const { return extent.intersects(box_); } @@ -211,23 +211,16 @@ struct filter_in_box struct filter_at_point { - coord2d pt_; - double tol_; - explicit filter_at_point(const coord2d& pt, double tol=0) - : pt_(pt), - tol_(tol) {} - bool pass(const box2d& extent) const + box2d box_; + explicit filter_at_point(coord2d const& pt, double tol=0) + : box_(pt,pt) { - if (tol_ == 0) - { - return extent.contains(pt_); - } - else - { - box2d extent2 = extent; - extent2.pad(tol_); - return extent2.contains(pt_); - } + box_.pad(tol); + } + + bool pass(box2d const& extent) const + { + return extent.intersects(box_); } }; From 57d5da9004a64e4e7fdd54c0bc0645588f0c4cde Mon Sep 17 00:00:00 2001 From: artemp Date: Fri, 17 May 2013 16:23:36 +0100 Subject: [PATCH 025/110] + fix sqlite.input build --- plugins/input/sqlite/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/input/sqlite/build.py b/plugins/input/sqlite/build.py index 72fb24a0f..db029e6ab 100644 --- a/plugins/input/sqlite/build.py +++ b/plugins/input/sqlite/build.py @@ -52,7 +52,7 @@ if env['PLUGIN_LINKING'] == 'shared': SHLIBSUFFIX='.input', source=plugin_sources, LIBS=libraries, - LINKFLAGS=join(' ').linkflags) + LINKFLAGS=(' ').join(linkflags)) # if the plugin links to libmapnik ensure it is built first Depends(TARGET, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME'])) From 551e9e427e9196e518d91c9beff6096f026c0855 Mon Sep 17 00:00:00 2001 From: artemp Date: Fri, 17 May 2013 16:24:32 +0100 Subject: [PATCH 026/110] + use tol in feature_at_point --- plugins/input/ogr/ogr_datasource.cpp | 4 ++-- plugins/input/ogr/ogr_index_featureset.cpp | 6 +++++- plugins/input/ogr/ogr_index_featureset.hpp | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/plugins/input/ogr/ogr_datasource.cpp b/plugins/input/ogr/ogr_datasource.cpp index b629d1e3d..8385598f4 100644 --- a/plugins/input/ogr/ogr_datasource.cpp +++ b/plugins/input/ogr/ogr_datasource.cpp @@ -82,7 +82,7 @@ void ogr_datasource::init(mapnik::parameters const& params) // initialize ogr formats OGRRegisterAll(); - + boost::optional file = params.get("file"); boost::optional string = params.get("string"); if (! file && ! string) @@ -525,7 +525,7 @@ featureset_ptr ogr_datasource::features_at_point(coord2d const& pt, double tol) if (indexed_) { - filter_at_point filter(pt); + filter_at_point filter(pt, tol); return featureset_ptr(new ogr_index_featureset (ctx, *layer, diff --git a/plugins/input/ogr/ogr_index_featureset.cpp b/plugins/input/ogr/ogr_index_featureset.cpp index cf95c5899..1f6c62939 100644 --- a/plugins/input/ogr/ogr_index_featureset.cpp +++ b/plugins/input/ogr/ogr_index_featureset.cpp @@ -59,7 +59,8 @@ ogr_index_featureset::ogr_index_featureset(mapnik::context_ptr const & layerdef_(layer.GetLayerDefn()), filter_(filter), tr_(new transcoder(encoding)), - fidcolumn_(layer_.GetFIDColumn()) + fidcolumn_(layer_.GetFIDColumn()), + feature_envelope_() { boost::optional memory = mapnik::mapped_memory_cache::instance().find(index_file, true); @@ -104,6 +105,9 @@ feature_ptr ogr_index_featureset::next() OGRGeometry* geom=poFeature->GetGeometryRef(); if (geom && !geom->IsEmpty()) { + geom->getEnvelope(&feature_envelope_); + if (!filter_.pass(mapnik::box2d(feature_envelope_.MinX,feature_envelope_.MinY, + feature_envelope_.MaxX,feature_envelope_.MaxY))) continue; ogr_converter::convert_geometry (geom, feature); } else diff --git a/plugins/input/ogr/ogr_index_featureset.hpp b/plugins/input/ogr/ogr_index_featureset.hpp index 5e4c6cbc5..9972935e4 100644 --- a/plugins/input/ogr/ogr_index_featureset.hpp +++ b/plugins/input/ogr/ogr_index_featureset.hpp @@ -50,6 +50,7 @@ private: std::vector::iterator itr_; boost::scoped_ptr tr_; const char* fidcolumn_; + OGREnvelope feature_envelope_; }; #endif // OGR_INDEX_FEATURESET_HPP From ef5e8f7fff98520bae33b6b332634f66ff0c63a0 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Fri, 17 May 2013 08:37:42 -0700 Subject: [PATCH 027/110] fix linking of python bindings when building against libmapnik.a --- bindings/python/build.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/bindings/python/build.py b/bindings/python/build.py index e32c9a410..2ba242c26 100644 --- a/bindings/python/build.py +++ b/bindings/python/build.py @@ -43,19 +43,22 @@ prefix = env['PREFIX'] target_path = os.path.normpath(env['PYTHON_INSTALL_LOCATION'] + os.path.sep + 'mapnik') target_path_deprecated = os.path.normpath(env['PYTHON_INSTALL_LOCATION'] + os.path.sep + 'mapnik2') -libraries = ['mapnik',env['BOOST_PYTHON_LIB']] +py_env = env.Clone() +py_env.Append(CPPPATH = env['PYTHON_INCLUDES']) + +py_env['LIBS'] = ['mapnik',env['BOOST_PYTHON_LIB']] # TODO - do solaris/fedora need direct linking too? if env['PLATFORM'] == 'Darwin': if not env['PYTHON_DYNAMIC_LOOKUP']: if env['PNG']: - libraries.append('png') + py_env.AppendUnique(LIBS = 'png') if env['JPEG']: - libraries.append('jpeg') - libraries.append(env['ICU_LIB_NAME']) - libraries.append('boost_regex%s' % env['BOOST_APPEND']) + py_env.AppendUnique(LIBS = 'jpeg') + py_env.AppendUnique(LIBS = env['ICU_LIB_NAME']) + py_env.AppendUnique(LIBS = 'boost_regex%s' % env['BOOST_APPEND']) if env['THREADING'] == 'multi': - libraries.append('boost_thread%s' % env['BOOST_APPEND']) + py_env.AppendUnique(LIBS = 'boost_thread%s' % env['BOOST_APPEND']) ##### Python linking on OS X is tricky ### # Confounding problems are: @@ -115,6 +118,9 @@ if env['CUSTOM_LDFLAGS']: else: linkflags = python_link_flag +if env['LINKING'] == 'static': + py_env.AppendUnique(LIBS=env['LIBMAPNIK_LIBS']) + paths = ''' """Configuration paths of Mapnik fonts and input plugins (auto-generated by SCons).""" @@ -147,9 +153,6 @@ except: pass # install the shared object beside the module directory sources = glob.glob('*.cpp') -py_env = env.Clone() -py_env.Append(CPPPATH = env['PYTHON_INCLUDES']) - if 'install' in COMMAND_LINE_TARGETS: # install the core mapnik python files, including '__init__.py' init_files = glob.glob('mapnik/*.py') @@ -183,8 +186,8 @@ if 'uninstall' not in COMMAND_LINE_TARGETS: py_env.ParseConfig('pkg-config --cflags pycairo') py_env.Append(CPPDEFINES = '-DHAVE_PYCAIRO') -libraries.append('boost_thread%s' % env['BOOST_APPEND']) -_mapnik = py_env.LoadableModule('mapnik/_mapnik', sources, LIBS=libraries, LDMODULEPREFIX='', LDMODULESUFFIX='.so',LINKFLAGS=linkflags) +py_env.AppendUnique(LIBS = 'boost_thread%s' % env['BOOST_APPEND']) +_mapnik = py_env.LoadableModule('mapnik/_mapnik', sources, LDMODULEPREFIX='', LDMODULESUFFIX='.so',LINKFLAGS=linkflags) Depends(_mapnik, env.subst('../../src/%s' % env['MAPNIK_LIB_NAME'])) From 2393453765cc9c96de0c0c5bce98984b5ef09f32 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Fri, 17 May 2013 09:23:10 -0700 Subject: [PATCH 028/110] scons: use VariantDir to avoid 'Two environments with different actions' error when building both cpp_tests and with PLUGIN_LINKING=static --- tests/cpp_tests/build.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/cpp_tests/build.py b/tests/cpp_tests/build.py index d47c167bc..8590b29d8 100644 --- a/tests/cpp_tests/build.py +++ b/tests/cpp_tests/build.py @@ -35,9 +35,10 @@ for cpp_test in glob.glob('*_test.cpp'): agg_env['CPPPATH'] = ['#deps/agg/include',env['BOOST_INCLUDES']] test_program = agg_env.Program(name, source=source_files, LINKFLAGS=env['CUSTOM_LDFLAGS']) else: + VariantDir('build','../../plugins/input/csv/') test_env_local = test_env.Clone() if 'csv_parse' in cpp_test: - source_files += glob.glob('../../plugins/input/csv/' + '*.cpp') + source_files += glob.glob('build' + '*.cpp') test_program = test_env_local.Program(name, source=source_files, LINKFLAGS=env['CUSTOM_LDFLAGS']) Depends(test_program, env.subst('../../src/%s' % env['MAPNIK_LIB_NAME'])) # build locally if installing From 03d8b6d9d79d25a1ac3400940b57c1b0416c147f Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Fri, 17 May 2013 13:16:36 -0700 Subject: [PATCH 029/110] msvc on windows breaks with singleton declared but gcc needs default visibility to avoid double singleton registration across dlls when using -fvisiblity=hidden --- include/mapnik/utils.hpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/include/mapnik/utils.hpp b/include/mapnik/utils.hpp index bbfcf7bd9..552fafaf3 100644 --- a/include/mapnik/utils.hpp +++ b/include/mapnik/utils.hpp @@ -94,9 +94,16 @@ public: } }; +#ifdef __GNUC__ template class CreatePolicy=CreateStatic> class MAPNIK_EXP singleton + template class CreatePolicy=CreateStatic> class MAPNIK_DECL singleton { +#else +template class CreatePolicy=CreateStatic> class singleton +{ +#endif + #ifdef __SUNPRO_CC /* Sun's C++ compiler will issue the following errors if CreatePolicy is used: Error: A class template name was expected instead of mapnik::CreatePolicy From 6af84da0ef345a8172b19f56adbbe76a0cda9dc5 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Fri, 17 May 2013 15:34:33 -0700 Subject: [PATCH 030/110] update out of date cairo reference images - refs #1781 --- ...rasterizer-600-400-1.0-cairo-reference.png | Bin 5859 -> 7639 bytes ...rasterizer-600-400-2.0-cairo-reference.png | Bin 10668 -> 13794 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/visual_tests/images/text-halo-rasterizer-600-400-1.0-cairo-reference.png b/tests/visual_tests/images/text-halo-rasterizer-600-400-1.0-cairo-reference.png index c4c6ddc7fbfccb35fa76d691e16bbbaed34f885e..05bb4b8523e6e977138249afda8058a04f55768e 100644 GIT binary patch literal 7639 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 iT`9CrP?va-M1I)eOU;h`8qV&E1 diff --git a/tests/visual_tests/images/text-halo-rasterizer-600-400-2.0-cairo-reference.png b/tests/visual_tests/images/text-halo-rasterizer-600-400-2.0-cairo-reference.png index e5cb5b699e5b7fdb23cccca63c4d17d9852c7028..b4fd03c3579bfd22385990d78088b1d786521a74 100644 GIT binary patch literal 13794 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 literal 10668 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

p1wau#fAI9t)FH zgZXvd*Iv52-pk<66&E9PAfKC^qikuc{;O`xhv#tlR^VF*jbQ#=(^H7*!?VjrDvFv4 J)pBNE{tZJeYMTH6 From ab27707a91b1bbee53e0f628122edb59c80089f8 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Fri, 17 May 2013 10:07:06 -0700 Subject: [PATCH 031/110] scons: reduce noisy, non-critical configure output --- SConstruct | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/SConstruct b/SConstruct index 73a650874..d682be1ba 100644 --- a/SConstruct +++ b/SConstruct @@ -737,25 +737,23 @@ def FindBoost(context, prefixes, thread_flag): msg = str() if BOOST_LIB_DIR: - msg += '\n *libs found: %s' % BOOST_LIB_DIR + msg += '\nFound boost libs: %s' % BOOST_LIB_DIR env['BOOST_LIBS'] = BOOST_LIB_DIR else: env['BOOST_LIBS'] = '/usr/' + env['LIBDIR_SCHEMA'] - msg += '\n *using default boost lib dir: %s' % env['BOOST_LIBS'] + msg += '\nUsing default boost lib dir: %s' % env['BOOST_LIBS'] if BOOST_INCLUDE_DIR: - msg += '\n *headers found: %s' % BOOST_INCLUDE_DIR + msg += '\nFound boost headers: %s' % BOOST_INCLUDE_DIR env['BOOST_INCLUDES'] = BOOST_INCLUDE_DIR else: env['BOOST_INCLUDES'] = '/usr/include' - msg += '\n *using default boost include dir: %s' % env['BOOST_INCLUDES'] + msg += '\nUsing default boost include dir: %s' % env['BOOST_INCLUDES'] if not env['BOOST_TOOLKIT'] and not env['BOOST_ABI'] and not env['BOOST_VERSION']: if BOOST_APPEND: - msg += '\n *lib naming extension found: %s' % BOOST_APPEND + msg += '\nFound boost lib name extension: %s' % BOOST_APPEND env['BOOST_APPEND'] = BOOST_APPEND - else: - msg += '\n *no lib naming extension found' else: # Creating BOOST_APPEND according to the Boost library naming order, # which goes ---. See: @@ -770,7 +768,7 @@ def FindBoost(context, prefixes, thread_flag): # Boost libraries. if len(append_params) > 1: env['BOOST_APPEND'] = '-'.join(append_params) - msg += '\n *using boost lib naming: %s' % env['BOOST_APPEND'] + msg += '\nFound boost lib name extension: %s' % env['BOOST_APPEND'] env.AppendUnique(CPPPATH = os.path.realpath(env['BOOST_INCLUDES'])) env.AppendUnique(LIBPATH = os.path.realpath(env['BOOST_LIBS'])) @@ -1221,7 +1219,7 @@ if not preconfigured: # if requested, sort LIBPATH and CPPPATH before running CheckLibWithHeader tests if env['PRIORITIZE_LINKING']: - conf.prioritize_paths(silent=False) + conf.prioritize_paths(silent=True) if not env['HOST']: for libname, headers, required, lang in LIBSHEADERS: @@ -1270,7 +1268,7 @@ if not preconfigured: # if requested, sort LIBPATH and CPPPATH before running CheckLibWithHeader tests if env['PRIORITIZE_LINKING']: - conf.prioritize_paths() + conf.prioritize_paths(silent=True) if not env['HOST']: # if the user is not setting custom boost configuration @@ -1679,7 +1677,7 @@ if not preconfigured: # if requested, sort LIBPATH and CPPPATH one last time before saving... if env['PRIORITIZE_LINKING']: - conf.prioritize_paths() + conf.prioritize_paths(silent=True) # finish config stage and pickle results env = conf.Finish() From 2e89e17687388a5efca23df15d3b5b23db8af204 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Fri, 17 May 2013 16:46:42 -0700 Subject: [PATCH 032/110] first pass on an html report to view failures --- tests/visual_tests/html_report_template.html | 33 ++++++++++++++++++++ tests/visual_tests/test.py | 30 +++++++++++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 tests/visual_tests/html_report_template.html diff --git a/tests/visual_tests/html_report_template.html b/tests/visual_tests/html_report_template.html new file mode 100644 index 000000000..759b8fe40 --- /dev/null +++ b/tests/visual_tests/html_report_template.html @@ -0,0 +1,33 @@ + + + + + + + +
+
expected
+
% difference
+
actual
+{{RESULTS}} +
+ + diff --git a/tests/visual_tests/test.py b/tests/visual_tests/test.py index 7ed969245..573fce489 100755 --- a/tests/visual_tests/test.py +++ b/tests/visual_tests/test.py @@ -18,7 +18,7 @@ visual_output_dir = "/tmp/mapnik-visual-images" defaults = { 'sizes': [(500, 100)], 'scales':[1.0,2.0], - 'agg': True, + 'agg': False, 'cairo': mapnik.has_cairo(), 'grid': True, } @@ -150,10 +150,29 @@ class Reporting: else: print '\x1b[31m✘\x1b[0m (\x1b[34m%s\x1b[0m)' % message + def make_html_item(self,error): + item = ''' +
+ ''' % (error[2],error[2],'%') + item += '
%s
' % (error[3]) + item += ''' +
+ + + +
+ ''' % (error[1],error[1],'%') + return (error[3],item) + def summary(self): if len(self.errors) == 0: print '\nAll %s visual tests passed: \x1b[1;32m✓ \x1b[0m' % self.passed return 0 + html_items = [] print "\nVisual rendering: %s failed / %s passed" % (len(self.errors), self.passed) for idx, error in enumerate(self.errors): if error[0] == self.OTHER: @@ -163,8 +182,17 @@ class Reporting: continue elif error[0] == self.DIFF: print str(idx+1) + ") \x1b[34m%s different pixels\x1b[0m:\n\t%s (\x1b[31mactual\x1b[0m)\n\t%s (\x1b[32mexpected\x1b[0m)" % (error[3], error[1], error[2]) + html_items.append(self.make_html_item(error)) elif error[0] == self.REPLACE: print str(idx+1) + ") \x1b[31mreplaced reference with new version:\x1b[0m %s" % error[2] + if len(html_items): + html_template = open(os.path.join(dirname,'html_report_template.html'),'r').read() + name = 'visual-tests-comparison.html' + html_out = open(name,'w+') + html_items.sort(reverse=True) + html_body = ''.join([a[1] for a in html_items]) + html_out.write(html_template.replace('{{RESULTS}}',html_body)) + print 'View failures by opening %s' % name return 1 def render_cairo(m, output, scale_factor): From 4c05d3a617edb45d06e3dfa1d6e31c24b2fdca60 Mon Sep 17 00:00:00 2001 From: artemp Date: Mon, 20 May 2013 11:48:13 +0100 Subject: [PATCH 033/110] + mapnik_datasource parameters : handle unicode objects - convert to internal UTF8 representation --- bindings/python/mapnik_datasource.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/bindings/python/mapnik_datasource.cpp b/bindings/python/mapnik_datasource.cpp index 95c495aec..07383b372 100644 --- a/bindings/python/mapnik_datasource.cpp +++ b/bindings/python/mapnik_datasource.cpp @@ -48,14 +48,26 @@ namespace { //user-friendly wrapper that uses Python dictionary using namespace boost::python; -boost::shared_ptr create_datasource(const dict& d) +boost::shared_ptr create_datasource(dict const& d) { mapnik::parameters params; boost::python::list keys=d.keys(); - for (int i=0; i(keys[i]); object obj = d[key]; + if (PyUnicode_Check(obj.ptr())) + { + PyObject* temp = PyUnicode_AsUTF8String(obj.ptr()); + if (temp) + { + char* c_str = PyString_AsString(temp); + params[key] = std::string(c_str); + Py_DecRef(temp); + } + continue; + } + extract ex0(obj); extract ex1(obj); extract ex2(obj); From 6412b43d6c84359f37649fa8049c686c10192f03 Mon Sep 17 00:00:00 2001 From: artemp Date: Mon, 20 May 2013 12:05:22 +0100 Subject: [PATCH 034/110] + utf8 to/from utf16 implementation --- include/mapnik/utils.hpp | 147 ++++++++++++++++++++++++--------------- 1 file changed, 92 insertions(+), 55 deletions(-) diff --git a/include/mapnik/utils.hpp b/include/mapnik/utils.hpp index 552fafaf3..5c2dde914 100644 --- a/include/mapnik/utils.hpp +++ b/include/mapnik/utils.hpp @@ -31,6 +31,7 @@ #endif // stl +#include #include #include #include @@ -40,6 +41,7 @@ namespace mapnik { + #ifdef MAPNIK_THREADSAFE using boost::mutex; #endif @@ -84,10 +86,10 @@ public: return new(&staticMemory) T; } #ifdef __SUNPRO_CC - // Sun C++ Compiler doesn't handle `volatile` keyword same as GCC. +// Sun C++ Compiler doesn't handle `volatile` keyword same as GCC. static void destroy(T* obj) #else - static void destroy(volatile T* obj) + static void destroy(volatile T* obj) #endif { obj->~T(); @@ -99,78 +101,113 @@ template class CreatePolicy=CreateStatic> class MAPNIK_DECL singleton { #else -template class CreatePolicy=CreateStatic> class singleton -{ + template class CreatePolicy=CreateStatic> class singleton + { #endif #ifdef __SUNPRO_CC - /* Sun's C++ compiler will issue the following errors if CreatePolicy is used: - Error: A class template name was expected instead of mapnik::CreatePolicy - Error: A "friend" declaration must specify a class or function. - */ - friend class CreatePolicy; +/* Sun's C++ compiler will issue the following errors if CreatePolicy is used: + Error: A class template name was expected instead of mapnik::CreatePolicy + Error: A "friend" declaration must specify a class or function. +*/ + friend class CreatePolicy; #else - friend class CreatePolicy; + friend class CreatePolicy; #endif - static T* pInstance_; - static bool destroyed_; - singleton(const singleton &rhs); - singleton& operator=(const singleton&); + static T* pInstance_; + static bool destroyed_; + singleton(const singleton &rhs); + singleton& operator=(const singleton&); - static void onDeadReference() - { - throw std::runtime_error("dead reference!"); - } - - static void DestroySingleton() - { - CreatePolicy::destroy(pInstance_); - pInstance_ = 0; - destroyed_ = true; - } - -protected: -#ifdef MAPNIK_THREADSAFE - static mutex mutex_; -#endif - singleton() {} -public: - static T& instance() - { - if (! pInstance_) + static void onDeadReference() { + throw std::runtime_error("dead reference!"); + } + + static void DestroySingleton() + { + CreatePolicy::destroy(pInstance_); + pInstance_ = 0; + destroyed_ = true; + } + + protected: #ifdef MAPNIK_THREADSAFE - mutex::scoped_lock lock(mutex_); + static mutex mutex_; #endif + singleton() {} + public: + static T& instance() + { if (! pInstance_) { - if (destroyed_) +#ifdef MAPNIK_THREADSAFE + mutex::scoped_lock lock(mutex_); +#endif + if (! pInstance_) { - destroyed_ = false; - onDeadReference(); - } - else - { - pInstance_ = CreatePolicy::create(); + if (destroyed_) + { + destroyed_ = false; + onDeadReference(); + } + else + { + pInstance_ = CreatePolicy::create(); - // register destruction - std::atexit(&DestroySingleton); + // register destruction + std::atexit(&DestroySingleton); + } } } + return *pInstance_; } - return *pInstance_; - } -}; + }; #ifdef MAPNIK_THREADSAFE -template class CreatePolicy> mutex singleton::mutex_; + template class CreatePolicy> mutex singleton::mutex_; #endif -template class CreatePolicy> T* singleton::pInstance_=0; -template class CreatePolicy> bool singleton::destroyed_=false; + template class CreatePolicy> T* singleton::pInstance_=0; + template class CreatePolicy> bool singleton::destroyed_=false; + + +#ifdef _WINDOWS + +#include + + // UTF8 <--> UTF16 conversion routines + + std::string utf16_to_utf8(std::wstring const& wstr) + { + std::string str; + int size = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, 0, 0, 0, 0); + if(size > 0) + { + std::vector buffer(size); + WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, &buffer[0], size, 0, 0); + str.assign(buffer.begin(), buffer.end() - 1); + } + return str; + } + + std::wstring utf8_to_utf16 (std::string const& str) + { + std::wstring wstr; + int size = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, 0, 0); + if (size > 0) + { + std::vector buffer(size); + MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &buffer[0], size); + wstr.assign(buffer.begin(), buffer.end() - 1); + } + return wstr; + } + +#endif // _WINDOWS } From 15e059a9acb79ef20c449aa2b08d3b7f1cf2e6bd Mon Sep 17 00:00:00 2001 From: "artem@windows" Date: Mon, 20 May 2013 13:43:31 -0700 Subject: [PATCH 035/110] put implementatio into .cpp to avoid exposing Conflicts: include/mapnik/utils.hpp --- include/mapnik/utils.hpp | 32 ++------------------- src/utils.cpp | 61 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 29 deletions(-) create mode 100644 src/utils.cpp diff --git a/include/mapnik/utils.hpp b/include/mapnik/utils.hpp index 5c2dde914..a9f013f5b 100644 --- a/include/mapnik/utils.hpp +++ b/include/mapnik/utils.hpp @@ -31,7 +31,6 @@ #endif // stl -#include #include #include #include @@ -177,35 +176,10 @@ template +// UTF8 <--> UTF16 conversion routines - // UTF8 <--> UTF16 conversion routines - - std::string utf16_to_utf8(std::wstring const& wstr) - { - std::string str; - int size = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, 0, 0, 0, 0); - if(size > 0) - { - std::vector buffer(size); - WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, &buffer[0], size, 0, 0); - str.assign(buffer.begin(), buffer.end() - 1); - } - return str; - } - - std::wstring utf8_to_utf16 (std::string const& str) - { - std::wstring wstr; - int size = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, 0, 0); - if (size > 0) - { - std::vector buffer(size); - MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &buffer[0], size); - wstr.assign(buffer.begin(), buffer.end() - 1); - } - return wstr; - } +MAPNIK_DECL std::string utf16_to_utf8(std::wstring const& wstr); +MAPNIK_DECL std::wstring utf8_to_utf16(std::string const& str); #endif // _WINDOWS diff --git a/src/utils.cpp b/src/utils.cpp new file mode 100644 index 000000000..a10102889 --- /dev/null +++ b/src/utils.cpp @@ -0,0 +1,61 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2013 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#ifdef _WINDOWS +// windows specific methods for UTF8 from/to UTF16 +#include +#include +#include + +#include + +namespace mapnik { + +std::string utf16_to_utf8(std::wstring const& wstr) +{ + std::string str; + int size = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, 0, 0, 0, 0); + if(size > 0) + { + std::vector buffer(size); + WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, &buffer[0], size, 0, 0); + str.assign(buffer.begin(), buffer.end() - 1); + } + return str; +} + +std::wstring utf8_to_utf16 (std::string const& str) +{ + std::wstring wstr; + int size = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, 0, 0); + if (size > 0) + { + std::vector buffer(size); + MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &buffer[0], size); + wstr.assign(buffer.begin(), buffer.end() - 1); + } + return wstr; +} + +} // namespace mapnik + +#endif // _WINDOWS From 735363c47d0d35f8a90479ea4ba1f53e164fdb22 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 20 May 2013 11:08:40 -0700 Subject: [PATCH 036/110] Revert "scons: use VariantDir to avoid 'Two environments with different actions' error when building both cpp_tests and with PLUGIN_LINKING=static" This reverts commit 2393453765cc9c96de0c0c5bce98984b5ef09f32. --- tests/cpp_tests/build.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/cpp_tests/build.py b/tests/cpp_tests/build.py index 8590b29d8..d47c167bc 100644 --- a/tests/cpp_tests/build.py +++ b/tests/cpp_tests/build.py @@ -35,10 +35,9 @@ for cpp_test in glob.glob('*_test.cpp'): agg_env['CPPPATH'] = ['#deps/agg/include',env['BOOST_INCLUDES']] test_program = agg_env.Program(name, source=source_files, LINKFLAGS=env['CUSTOM_LDFLAGS']) else: - VariantDir('build','../../plugins/input/csv/') test_env_local = test_env.Clone() if 'csv_parse' in cpp_test: - source_files += glob.glob('build' + '*.cpp') + source_files += glob.glob('../../plugins/input/csv/' + '*.cpp') test_program = test_env_local.Program(name, source=source_files, LINKFLAGS=env['CUSTOM_LDFLAGS']) Depends(test_program, env.subst('../../src/%s' % env['MAPNIK_LIB_NAME'])) # build locally if installing From 29578410c27254de2050fb7a5a708683ae577225 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 20 May 2013 12:05:13 -0700 Subject: [PATCH 037/110] fix marker bbox calculation - closes #1849 --- include/mapnik/marker.hpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/include/mapnik/marker.hpp b/include/mapnik/marker.hpp index d9c1fe970..568f56550 100644 --- a/include/mapnik/marker.hpp +++ b/include/mapnik/marker.hpp @@ -100,17 +100,25 @@ public: inline double width() const { if (is_bitmap()) + { return (*bitmap_data_)->width(); + } else if (is_vector()) - return (*vector_data_)->width(); + { + return (*vector_data_)->bounding_box().width(); + } return 0; } inline double height() const { if (is_bitmap()) + { return (*bitmap_data_)->height(); + } else if (is_vector()) - return (*vector_data_)->height(); + { + return (*vector_data_)->bounding_box().height(); + } return 0; } From 84a15f90903824bddb262c966973de8b52d47478 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 20 May 2013 12:44:55 -0700 Subject: [PATCH 038/110] iwyu --- include/mapnik/debug_symbolizer.hpp | 1 + src/feature_type_style.cpp | 1 + src/gamma_method.cpp | 1 + src/gradient.cpp | 1 + src/markers_symbolizer.cpp | 1 + src/polygon_pattern_symbolizer.cpp | 1 + src/raster_colorizer.cpp | 1 + src/stroke.cpp | 1 + src/well_known_srs.cpp | 1 + 9 files changed, 9 insertions(+) diff --git a/include/mapnik/debug_symbolizer.hpp b/include/mapnik/debug_symbolizer.hpp index 91ea152d2..e9e5d3c2b 100644 --- a/include/mapnik/debug_symbolizer.hpp +++ b/include/mapnik/debug_symbolizer.hpp @@ -25,6 +25,7 @@ #include #include +#include namespace mapnik { diff --git a/src/feature_type_style.cpp b/src/feature_type_style.cpp index f041b714f..f850a86f8 100644 --- a/src/feature_type_style.cpp +++ b/src/feature_type_style.cpp @@ -22,6 +22,7 @@ #include #include +#include // boost #include diff --git a/src/gamma_method.cpp b/src/gamma_method.cpp index 4a7fad31f..f1adf99e2 100644 --- a/src/gamma_method.cpp +++ b/src/gamma_method.cpp @@ -22,6 +22,7 @@ // mapnik #include +#include namespace mapnik { diff --git a/src/gradient.cpp b/src/gradient.cpp index 3af55050b..41612833b 100644 --- a/src/gradient.cpp +++ b/src/gradient.cpp @@ -21,6 +21,7 @@ *****************************************************************************/ #include +#include namespace mapnik { diff --git a/src/markers_symbolizer.cpp b/src/markers_symbolizer.cpp index 0bb371cb1..6c48effd2 100644 --- a/src/markers_symbolizer.cpp +++ b/src/markers_symbolizer.cpp @@ -25,6 +25,7 @@ #include #include #include +#include namespace mapnik { diff --git a/src/polygon_pattern_symbolizer.cpp b/src/polygon_pattern_symbolizer.cpp index 4a2e60c31..67449890e 100644 --- a/src/polygon_pattern_symbolizer.cpp +++ b/src/polygon_pattern_symbolizer.cpp @@ -22,6 +22,7 @@ // mapnik #include +#include namespace mapnik { diff --git a/src/raster_colorizer.cpp b/src/raster_colorizer.cpp index 25cacf679..fbacb1212 100644 --- a/src/raster_colorizer.cpp +++ b/src/raster_colorizer.cpp @@ -27,6 +27,7 @@ #include #include #include +#include // stl #include diff --git a/src/stroke.cpp b/src/stroke.cpp index acaf6c93b..deb4ce625 100644 --- a/src/stroke.cpp +++ b/src/stroke.cpp @@ -21,6 +21,7 @@ *****************************************************************************/ #include +#include namespace mapnik { diff --git a/src/well_known_srs.cpp b/src/well_known_srs.cpp index a6e8d0ae6..c3163df0d 100644 --- a/src/well_known_srs.cpp +++ b/src/well_known_srs.cpp @@ -23,6 +23,7 @@ // mapnik #include #include +#include // boost #include From c901f8b46e0fe419c5158c24c7c8f964786e8410 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 20 May 2013 12:45:06 -0700 Subject: [PATCH 039/110] tests for 1849 --- ...g-eq-width-600-400-1.0-grid-reference.json | 109 ++++++++++++++ ...on-polygon-500-100-1.0-grid-reference.json | 39 +++++ ...on-polygon-600-400-1.0-grid-reference.json | 115 +++++++++++++++ ...g-eq-width-600-400-1.0-cairo-reference.png | Bin 0 -> 3280 bytes ...g-eq-width-600-400-2.0-cairo-reference.png | Bin 0 -> 3872 bytes ...on-polygon-500-100-1.0-cairo-reference.png | Bin 0 -> 3443 bytes ...on-polygon-500-100-2.0-cairo-reference.png | Bin 0 -> 6009 bytes ...on-polygon-600-400-1.0-cairo-reference.png | Bin 0 -> 10551 bytes ...on-polygon-600-400-2.0-cairo-reference.png | Bin 0 -> 16077 bytes .../shield-on-line-spacing-eq-width.xml | 78 ++++++++++ .../visual_tests/styles/shield-on-polygon.xml | 49 +++++++ tests/visual_tests/test.py | 134 +++++++++--------- 12 files changed, 459 insertions(+), 65 deletions(-) create mode 100644 tests/visual_tests/grids/shield-on-line-spacing-eq-width-600-400-1.0-grid-reference.json create mode 100644 tests/visual_tests/grids/shield-on-polygon-500-100-1.0-grid-reference.json create mode 100644 tests/visual_tests/grids/shield-on-polygon-600-400-1.0-grid-reference.json create mode 100644 tests/visual_tests/images/shield-on-line-spacing-eq-width-600-400-1.0-cairo-reference.png create mode 100644 tests/visual_tests/images/shield-on-line-spacing-eq-width-600-400-2.0-cairo-reference.png create mode 100644 tests/visual_tests/images/shield-on-polygon-500-100-1.0-cairo-reference.png create mode 100644 tests/visual_tests/images/shield-on-polygon-500-100-2.0-cairo-reference.png create mode 100644 tests/visual_tests/images/shield-on-polygon-600-400-1.0-cairo-reference.png create mode 100644 tests/visual_tests/images/shield-on-polygon-600-400-2.0-cairo-reference.png create mode 100644 tests/visual_tests/styles/shield-on-line-spacing-eq-width.xml create mode 100644 tests/visual_tests/styles/shield-on-polygon.xml diff --git a/tests/visual_tests/grids/shield-on-line-spacing-eq-width-600-400-1.0-grid-reference.json b/tests/visual_tests/grids/shield-on-line-spacing-eq-width-600-400-1.0-grid-reference.json new file mode 100644 index 000000000..25c5fc711 --- /dev/null +++ b/tests/visual_tests/grids/shield-on-line-spacing-eq-width-600-400-1.0-grid-reference.json @@ -0,0 +1,109 @@ +{ + "keys": [ + "", + "1" + ], + "data": {}, + "grid": [ + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " !", + " !! ", + " !! ", + " !! ", + " !! ", + " !! ", + " !! ", + " !! ", + " !! ", + " !! ", + " !! ", + " !! ", + " !! ", + " !!!!! !! ", + " !!!!! !! ", + " !!!!! ", + " !! !!!!! ", + " !! ", + " !! ", + " !! ", + " !! ", + " !! ", + " !! ", + " !! ", + " !! ", + " !! ", + " !! ", + " !! ", + " !! ", + " !! ", + "! ", + " ", + " ", + " ", + " " + ] +} \ No newline at end of file diff --git a/tests/visual_tests/grids/shield-on-polygon-500-100-1.0-grid-reference.json b/tests/visual_tests/grids/shield-on-polygon-500-100-1.0-grid-reference.json new file mode 100644 index 000000000..83e2d2aaa --- /dev/null +++ b/tests/visual_tests/grids/shield-on-polygon-500-100-1.0-grid-reference.json @@ -0,0 +1,39 @@ +{ + "keys": [ + "", + "1", + "3", + "2", + "4", + "5", + "6" + ], + "data": {}, + "grid": [ + " ! ", + " !! ", + " !!! ", + " !! # ", + " !!! ", + " !!!! ", + " !!!! !! ", + " !!!!!!!! ", + " !!!!!!!! ", + " !!!!!!!!! ", + " !!!!!!! ", + " !!!! ", + " $$ % !!! ", + " $$$$$!!! ", + " $$$$$$ ! ", + " $$$$$$ ", + " $$$$$$ ", + " $$$$$$ ", + " $$$$$$$$ ", + " $$$$$$$$ ", + " $$$$$$$$$ ", + " $$$$$$$$$ ", + " &$$$$$$$$ ", + " $$$$$$$$ ", + " '' $ " + ] +} \ No newline at end of file diff --git a/tests/visual_tests/grids/shield-on-polygon-600-400-1.0-grid-reference.json b/tests/visual_tests/grids/shield-on-polygon-600-400-1.0-grid-reference.json new file mode 100644 index 000000000..176d251f7 --- /dev/null +++ b/tests/visual_tests/grids/shield-on-polygon-600-400-1.0-grid-reference.json @@ -0,0 +1,115 @@ +{ + "keys": [ + "", + "1", + "3", + "2", + "4", + "7", + "5", + "6" + ], + "data": {}, + "grid": [ + " !!! ", + " !!! ", + " !! ", + " !! ! ", + " !!!!! ", + " !!!!!!!! ", + " !!!!!!!! ! ", + " !!!!!!!!! ", + " !!!!!!!!!! ", + " !!!!!!!!!! ", + " !!!!!!!!! ", + " !!!!!!! ", + " !!!!!!! ", + " !!!!!!! ## ", + " !!!!!!! ## ", + " !!!!!!! ", + " !!!!! ! ", + " !!!!! !! ", + " !!! !!! ", + " !!!!!! !!!! ", + " !!!!!!! !!! ", + " !!!!!!! !!! ", + " !!!!!!!!!!! ", + " !!!!!!!!!!! ", + " !!!!!!!!!!!! !!! ", + " !!!!!!!!!!!! !!!!!!! ", + " !!!!!!!!!!!!!! !!!!!!!! ", + " !!!!!!!!!!!!!!!!!! !!!!!!!! ", + " !!!!!!!!!!!!!!!!!!!!!!!!!!!!! ", + " !!!!!!!!!!!!!!!!!!!!!!!!!!!!! ", + " !!!!!!!!!!!!!!!!!!!!!!!!!!!!! ", + " !!!!!!!!!!!!!!!!!!!!!!!!!!!!! ", + " !!!!!!!!!!!!!!!!!!!!!!!!!!!!! ", + " !!!!!!!!!!!!!!!!!!!!!!!!!!!!! ", + " !!!!!!!!!!!!!!!!!!!!!!!!!!! ", + " !!!!!!!!!!!!!!!!!!!!!!!!!!!!! ", + " !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ", + " !!!!!!!!!!!!!!!!!!!!!!!!!!! ! ", + " !!!!!!!!!!!!!!!!!!!!!!!!!! ", + " !!!!!!!!!!!!!!!!!!!!!!!!! ", + " !!!!!!!!!!!!!!!!!!!!!!!! ", + " !!!!!!!!!!!!!!!!!!!!!! ", + " !!!!!!!!!!!!!!!!!!!! ", + " !!!!!!!!!!!!!!!! ", + " !!!!!!!!!!!!!!! ", + " !!!!!!!!!!!!! ", + " !!!!!!!!!!!! ", + " $$$ !!!!!!!!!!!! ", + " $$ !!!!!!!!!!! ", + " $$$$$ %% !!!!!!!!!!! ", + " $$$$$$$$ %% !!!!!!!!!!! ", + " $$$$$$$$ $$$$$$ !!!!!!!!!!! ", + " $$$$$$$$ $$$$$$$$ !!!!!!!!!!! ", + " $$$$$$$$$$$$$$$$$$ !!!!!!!!!!! ", + " $$$$$$$$$$$$$$$$$$ !!!!!!!! ", + " $$$$$$$$$$$$$$$$$ !!!! ", + " $$$$$$$$$$$$$$$$$$$ ! ", + " $$$$$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ", + " &&$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ", + " ''$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ", + " ''$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$$$$$$$$$$$$$ ", + " $$$$$$ $$$$$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$$$$$ ", + " $$$$$$$$$$$$ ", + " $$ $$$$$ ", + " ((( ", + " (((( ", + " (((((( ", + " (((((( " + ] +} \ No newline at end of file diff --git a/tests/visual_tests/images/shield-on-line-spacing-eq-width-600-400-1.0-cairo-reference.png b/tests/visual_tests/images/shield-on-line-spacing-eq-width-600-400-1.0-cairo-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..49bfde80257eb97a01ef4c9c108c32a1f2c74d52 GIT binary patch literal 3280 zcmai1d00~U8m7?-L9b|r)8ayEj!HV2+BS-rW|~Hc+R?fiHMS^4mP<)QqmT=umZnXV zQkuI4=7>vai8_u{I|fgV`H<`+Is8OtynC}8g-UxV6X=je9FuWg@ScaGAmTKJxbz= zdb(;Aj7Gya98A*d#^d4d@UnI5U=tHKJUsltv17KjaC9`w$q8n$SkBIH+1a!Fh={nj zI1dlFGCVvfDd}c%a%yTS1i`_<@QD-fojZ4UJRY46SJ0ncxBy2*!JR273re zy{o?*^i$^J)w*MzB-EHIv_DpOQ=LlC5WDEIIi_f=2M(WROf=o|UxIr2B{Wti;tq|? zLDz0MskE7~(ZJpl#<#1_J(I(AkuwGLtf(xZP$x{~1u0b@jxM!GXIs*j*NDaYN@d%m zSG(cqQ_|;Ss=8$(_DWJo72U8mUHM$5vQf38vDf_R(zB5MW_Z3U-6)7u%}f^l88WA+ zTlfeENu}`Tf)>G6&bM}N6s0$V z+hi5E%u_q7+Lyb>fzVpRR1_Rk0-*sBELT>wUo+LnHb}ClW-3e)zn0$Q-v@&K&VH4)q>9M)%T57;@2&Qv ziU=CtttswIvP4#qa2Z#2xCYc&I*%#>Or16}1N)q~O)ZbeSfu|(a5}J3yL_M4d467O zJe0ets@(+EXA7D%lwC;>(e2N*n;VGe zdVgQU)6I(HoW*g@ah1z=tQbOHJ&tXkAj5X_R{S$r#w{#6{dE0+z7BPZkf~$8H1f7S zqW{`7E!g&4e6zx=t2eAc5%M=e5G~kR61-j{&>OYU8aR@q5cbu({95NjkEqk`u!VwjPL{IK1_khz!AiT9z|t!D8#65(Ql-|{I;p1H zlDB~k`uaQ|_b!2BA1`QPsqqR#Gx}ktyeI(LetLC}AUc1wAL~bPt!Qa(#lULvW5&e$ zNxrO2yLfrNGr3vO5cfHo7autdH+7JPhKK8~IuS+MjmMPT$a|hD7*;Jy-gT7l%ln*m z%nwsGmqsg}Gfu*P&5ZBq;e!<#T9w%gvVq;ZcgtU~Vt8PMhSm?+G4m1q-A4HGEDf%# z5hZ0zSN#-M8oUXh-be}zaqp~O)it$UGt*QhE|<;I$&Db!fYH-#50N3v_j z7QM$h+tHiYgB1NN7A=2vn@d!O+~Q5ZK}eQ(jjV$|l_RCFId{jDdelnumyV+@P;E#} z!7=oKDx&f>mO7p}nthQ#bW9qeShMro%NY`maQ3?>at$uiuzAWtUtFACh;S386Tnxx zE3p>TmK@AMq%`s(k~f|B9LaO~g5_&+(jgI!$h2jfjEn^~UkYa+%Buy3RpkEp(yV2~?R7R+}_~M6bj4O10kn$7y zWirDON397buiQkODl$cL8I~I2_t~DY!s>#wGg|=X#lr$J)u9ovk6$Bn*G9b3cTX`O zEZteqAjNG=o69IhZ=r^hHF7r-rz`>@IL(e`Hy#6XQ{h&Sf$?33SKhulcLUIYb-aP zdHyNDy#BTsNK%Y|A&V{MK}o3RB%qJGf@sXPvj`Ey0|ZZB7q8S?wGvFmA-n@g4TK*+ z(vGhniF5cFSVsKq2jGq^2uH+pDJOm&gPWP;MC*Zrr$W7t�&=lE#Cx{xPB)s#Y z>}zr4I7d-#eBS<01Dk!{dtDpJVJ`%QRa@-s3^u;*S$bAtO#Jy>*X=#fUn4^RA^I`O zDk?Qki)V|*?sYf^{xdm%@qgKA2QXI`VHiFLLwSNQ zR@1=guVxFF%=2a-?LgTH?{)0F!*BC7#XkF-L6>nto<*F{7uN&&R;IrJxOPoHz{s>g zfIT`=fc8bXz+-v%Fz~k?ISMe*KLntMA042E?-hj4c?djBhmb9F$3i8>rD8o~oSlL9 zY|$m?9?&-9-ZE$_DZGVXZXQ6-$2ABRy+-iSJAg@oIe>wuZ9m!!=N3`em2FKk0!&_! z8MyO+o+^!)kxa#C)`m}wIYl$*u&i`pN`5dyIM+4%GX4Oy1H2c3E$qk&Qhg5`Q$Wg| zbuR#=Igg z83xxWmogf2e}XNWbdk)tspV+;q|vqd=*2)X6a;Lo16?vk%ODgyxQQ6du8f_R*OT>I zc2nm~h*ix3GcR=58g^vL+-LCfxPdEQz9qbh>kW7uJoCeRhH}7WA@_6zeR+{Uoi{E$ zvWjIeq}=|noGt8K8h%wsa)_fO$2ZHBd7bAWyA0oSYKL!38OM!s*G>=bJ=(nwkXZ}D zGsT*(fwIRFJd4g*Aa5$faPvq0(8YUewd^C&wCz}=jBj4ZU^Owb1X(N zY&Xi5-#6gB?M;qCc0HLJL1CP3yc?y=6BOES@qp|&ebf%UpA!J!-vhT$9jF&HG`RWS zpJ^1H!IUIxbMybC`a*UqgX@85-xoBHh9`iuVLg}-Cti#9Cj5dxiW^8v@IoSM8{^{C scrLV2*j#Za@&A78{?D&qU3m3;_0Y!UYl8A{JLvk@<#_DB01!=U6aWAK literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/shield-on-line-spacing-eq-width-600-400-2.0-cairo-reference.png b/tests/visual_tests/images/shield-on-line-spacing-eq-width-600-400-2.0-cairo-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..61e332ddf999789c4f47bd8324f446f1f88580f6 GIT binary patch literal 3872 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 g003kN0{{R3M;S6t0004uP)t-s0s;aC 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 Vj75uTt5*O3002ovPDHLkV1mBf_(A{x literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/shield-on-polygon-500-100-2.0-cairo-reference.png b/tests/visual_tests/images/shield-on-polygon-500-100-2.0-cairo-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..8ebad2380a99717d5f4c28a60b0bb0d540d95bae GIT binary patch literal 6009 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 literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/shield-on-polygon-600-400-1.0-cairo-reference.png b/tests/visual_tests/images/shield-on-polygon-600-400-1.0-cairo-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..f486e6c3e1da46c9591481ed4adc8f0f88a7b10f GIT binary patch literal 10551 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 aKqfTQmOkeZzC&GzL{mcD8>*5v3;7?b`N2T| literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/shield-on-polygon-600-400-2.0-cairo-reference.png b/tests/visual_tests/images/shield-on-polygon-600-400-2.0-cairo-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..0d4c6a74c55431cf96a381b9f409e900907848f6 GIT binary patch literal 16077 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 + + + + alpha + + csv + + wkt + "LINESTRING(0 0, 10 2)" + + + + + beta + + csv + + wkt + "LINESTRING(0 2, 10 4)" + + + + + gamma + + csv + + wkt + "LINESTRING(0 4, 10 6)" + + + + + + + + \ No newline at end of file diff --git a/tests/visual_tests/styles/shield-on-polygon.xml b/tests/visual_tests/styles/shield-on-polygon.xml new file mode 100644 index 000000000..c4f2f9e55 --- /dev/null +++ b/tests/visual_tests/styles/shield-on-polygon.xml @@ -0,0 +1,49 @@ + + + + + + -180,-85.05112877980659,180,85.05112877980659 + 0,0,2 + png + 0 + 22 + + + + + + + countries-outline + countries + + ../../data/shp/new_zealand/ne_50m_land.shp + + + + + + + places + + ../../data/shp/new_zealand/ne_50m_populated_places_simple.shp + + + + + \ No newline at end of file diff --git a/tests/visual_tests/test.py b/tests/visual_tests/test.py index 573fce489..c9601e7cc 100755 --- a/tests/visual_tests/test.py +++ b/tests/visual_tests/test.py @@ -18,9 +18,9 @@ visual_output_dir = "/tmp/mapnik-visual-images" defaults = { 'sizes': [(500, 100)], 'scales':[1.0,2.0], - 'agg': False, + 'agg': True, 'cairo': mapnik.has_cairo(), - 'grid': True, + 'grid': True } sizes_many_in_big_range = [(800, 100), (600, 100), (400, 100), @@ -34,69 +34,71 @@ default_text_box = mapnik.Box2d(-0.05, -0.01, 0.95, 0.01) dirname = os.path.dirname(__file__) -files = [ - {'name': "list", 'sizes': sizes_many_in_big_range,'bbox':default_text_box}, - {'name': "simple", 'sizes': sizes_many_in_big_range,'bbox':default_text_box}, - {'name': "lines-1", 'sizes': sizes_few_square,'bbox':default_text_box}, - {'name': "lines-2", 'sizes': sizes_few_square,'bbox':default_text_box}, - {'name': "lines-3", 'sizes': sizes_few_square,'bbox':default_text_box}, +files = { + 'list': {'sizes': sizes_many_in_big_range,'bbox':default_text_box}, + 'simple': {'sizes': sizes_many_in_big_range,'bbox':default_text_box}, + 'lines-1': {'sizes': sizes_few_square,'bbox':default_text_box}, + 'lines-2': {'sizes': sizes_few_square,'bbox':default_text_box}, + 'lines-3': {'sizes': sizes_few_square,'bbox':default_text_box}, # https://github.com/mapnik/mapnik/issues/1696 # https://github.com/mapnik/mapnik/issues/1521 # fails with clang++ on os x - {'name': "lines-shield", 'sizes': sizes_few_square,'bbox':default_text_box}, - {'name': "collision", 'sizes':[(600,400)]}, - {'name': "marker-svg-opacity"}, - {'name': "marker-multi-policy", 'sizes':[(600,400)]}, - {'name': "marker-on-line", 'sizes':[(600,400)], + 'lines-shield': {'sizes': sizes_few_square,'bbox':default_text_box}, + 'collision': {'sizes':[(600,400)]}, + 'shield-on-polygon': {'sizes':[(600,400)]}, + 'shield-on-line-spacing-eq-width': {'sizes':[(600,400)]}, + 'marker-svg-opacity':{}, + 'marker-multi-policy': {'sizes':[(600,400)]}, + 'marker-on-line': {'sizes':[(600,400)], 'bbox': mapnik.Box2d(-10, 0, 15, 20)}, - {'name': "marker-on-line-spacing-eq-width", 'sizes':[(600,400)]}, - {'name': "marker-on-line-spacing-eq-width-overlap", 'sizes':[(600,400)]}, - {'name': "marker_line_placement_on_points"}, - {'name': "marker-with-background-image", 'sizes':[(600,400),(400,600),(257,256)]}, - #{'name': "marker-with-background-image-and-hsla-transform", 'sizes':[(600,400),(400,600),(257,256)]}, - {'name': "marker-on-hex-grid", 'sizes':[(600,400),(400,600),(257,256)]}, - {'name': "whole-centroid", 'sizes':[(600,400)], + 'marker-on-line-spacing-eq-width': {'sizes':[(600,400)]}, + 'marker-on-line-spacing-eq-width-overlap': {'sizes':[(600,400)]}, + 'marker_line_placement_on_points':{}, + 'marker-with-background-image': {'sizes':[(600,400),(400,600),(257,256)]}, + #'marker-with-background-image-and-hsla-transform': {'sizes':[(600,400),(400,600),(257,256)]}, + 'marker-on-hex-grid': {'sizes':[(600,400),(400,600),(257,256)]}, + 'whole-centroid': {'sizes':[(600,400)], 'bbox': mapnik.Box2d(736908, 4390316, 2060771, 5942346)}, - {'name': "text-halo-rasterizer", 'sizes':[(600,400)]}, - {'name': "simple-E", 'bbox':mapnik.Box2d(-0.05, -0.01, 0.95, 0.01)}, - {'name': "simple-NE",'bbox':default_text_box}, - {'name': "simple-NW",'bbox':default_text_box}, - {'name': "simple-N",'bbox':default_text_box}, - {'name': "simple-SE",'bbox':default_text_box}, - {'name': "simple-SW",'bbox':default_text_box}, - {'name': "simple-S",'bbox':default_text_box}, - {'name': "simple-W",'bbox':default_text_box}, - {'name': "formatting-1",'bbox':default_text_box}, - {'name': "formatting-2",'bbox':default_text_box}, - {'name': "formatting-3",'bbox':default_text_box}, - {'name': "formatting-4",'bbox':default_text_box}, - {'name': "expressionformat",'bbox':default_text_box}, - {'name': "shieldsymbolizer-1", 'sizes': sizes_many_in_small_range,'bbox':default_text_box}, - {'name': "rtl-point", 'sizes': [(200, 200)],'bbox':default_text_box}, - {'name': "jalign-auto", 'sizes': [(200, 200)],'bbox':default_text_box}, - {'name': "line-offset", 'sizes':[(900, 250)],'bbox': mapnik.Box2d(-5.192, 50.189, -5.174, 50.195)}, - {'name': "tiff-alpha-gdal", 'sizes':[(600,400)]}, - {'name': "tiff-alpha-broken-assoc-alpha-gdal", 'sizes':[(600,400)]}, - {'name': "tiff-alpha-gradient-gdal", 'sizes':[(600,400)]}, - {'name': "tiff-nodata-edge-gdal", 'sizes':[(600,400),(969,793)]}, - {'name': "tiff-opaque-edge-gdal", 'sizes':[(256,256),(969,793)]}, - {'name': "tiff-opaque-edge-gdal2", 'sizes':[(600,400),(969,793)]}, - {'name': "tiff-opaque-edge-raster2", 'sizes':[(600,400),(969,793)]}, - {'name': "tiff-resampling", 'sizes':[(600,400)]}, + 'text-halo-rasterizer': {'sizes':[(600,400)]}, + 'simple-E': {'bbox':mapnik.Box2d(-0.05, -0.01, 0.95, 0.01)}, + 'simple-NE': {'bbox':default_text_box}, + 'simple-NW': {'bbox':default_text_box}, + 'simple-N': {'bbox':default_text_box}, + 'simple-SE': {'bbox':default_text_box}, + 'simple-SW': {'bbox':default_text_box}, + 'simple-S': {'bbox':default_text_box}, + 'simple-W': {'bbox':default_text_box}, + 'formatting-1': {'bbox':default_text_box}, + 'formatting-2': {'bbox':default_text_box}, + 'formatting-3': {'bbox':default_text_box}, + 'formatting-4': {'bbox':default_text_box}, + 'expressionformat': {'bbox':default_text_box}, + 'shieldsymbolizer-1': {'sizes': sizes_many_in_small_range,'bbox':default_text_box}, + 'rtl-point': {'sizes': [(200, 200)],'bbox':default_text_box}, + 'jalign-auto': {'sizes': [(200, 200)],'bbox':default_text_box}, + 'line-offset': {'sizes':[(900, 250)],'bbox': mapnik.Box2d(-5.192, 50.189, -5.174, 50.195)}, + 'tiff-alpha-gdal': {'sizes':[(600,400)]}, + 'tiff-alpha-broken-assoc-alpha-gdal': {'sizes':[(600,400)]}, + 'tiff-alpha-gradient-gdal': {'sizes':[(600,400)]}, + 'tiff-nodata-edge-gdal': {'sizes':[(600,400),(969,793)]}, + 'tiff-opaque-edge-gdal': {'sizes':[(256,256),(969,793)]}, + 'tiff-opaque-edge-gdal2': {'sizes':[(600,400),(969,793)]}, + 'tiff-opaque-edge-raster2': {'sizes':[(600,400),(969,793)]}, + 'tiff-resampling': {'sizes':[(600,400)]}, # https://github.com/mapnik/mapnik/issues/1622 - {'name': "tiff-edge-alignment-gdal1", 'sizes':[(256,256),(255,257)], + 'tiff-edge-alignment-gdal1': {'sizes':[(256,256),(255,257)], 'bbox':mapnik.Box2d(-13267022.12540147,4618019.500877209,-13247454.246160466,4637587.380118214) }, - {'name': "tiff-edge-alignment-gdal2", 'sizes':[(256,256),(255,257)], + 'tiff-edge-alignment-gdal2': {'sizes':[(256,256),(255,257)], 'bbox':mapnik.Box2d(-13267022.12540147,4598451.621636203,-13247454.246160466,4618019.500877209) }, # https://github.com/mapnik/mapnik/issues/1520 # commented because these are not critical failures - #{'name': "tiff-alpha-raster", 'sizes':[(600,400)]}, - #{'name': "tiff-alpha-broken-assoc-alpha-raster", 'sizes':[(600,400)]}, - #{'name': "tiff-nodata-edge-raster", 'sizes':[(600,400)]}, - #{'name': "tiff-opaque-edge-raster", 'sizes':[(256,256)]}, - ] + #'tiff-alpha-raster': {'sizes':[(600,400)]}, + #'tiff-alpha-broken-assoc-alpha-raster': {'sizes':[(600,400)]}, + #'tiff-nodata-edge-raster': {'sizes':[(600,400)]}, + #'tiff-opaque-edge-raster': {'sizes':[(256,256)]}, + } class Reporting: DIFF = 1 @@ -233,8 +235,7 @@ renderers = [ ] -def render(config, width, height, bbox, scale_factor, reporting): - filename = config['name'] +def render(filename,config, width, height, bbox, scale_factor, reporting): m = mapnik.Map(width, height) postfix = "%s-%d-%d-%s" % (filename, width, height, scale_factor) @@ -286,31 +287,34 @@ if __name__ == "__main__": else: overwrite_failures = False - if len(sys.argv) == 2: - files = [{"name": sys.argv[1], "sizes": sizes_few_square}] - elif len(sys.argv) > 2: - files = [] + select_files = {} + if len(sys.argv) > 1: for name in sys.argv[1:]: - files.append({"name": name}) + if name in files: + select_files[name]=files[name] + else: + select_files[name]={} + if len(select_files) > 0: + files = select_files if not os.path.exists(visual_output_dir): os.makedirs(visual_output_dir) - if 'osm' in mapnik.DatasourceCache.plugin_names(): reporting = Reporting(quiet, overwrite_failures) - for f in files: + for filename in files: config = dict(defaults) - config.update(f) + config.update(files[filename]) for size in config['sizes']: for scale_factor in config['scales']: - m = render(config, + m = render(filename, + config, size[0], size[1], config.get('bbox'), scale_factor, reporting) - mapnik.save_map(m, os.path.join(dirname, 'xml_output', "%s-out.xml" % config['name'])) + mapnik.save_map(m, os.path.join(dirname, 'xml_output', "%s-out.xml" % filename)) sys.exit(reporting.summary()) else: From c1e94120f9438c2e65ce3f5303834e0c1b87f474 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 20 May 2013 12:49:43 -0700 Subject: [PATCH 040/110] add more expected test files --- ...acing-eq-width-600-400-1.0-agg-reference.png | Bin 0 -> 3665 bytes ...acing-eq-width-600-400-2.0-agg-reference.png | Bin 0 -> 4090 bytes ...eld-on-polygon-600-400-1.0-agg-reference.png | Bin 0 -> 10334 bytes ...eld-on-polygon-600-400-2.0-agg-reference.png | Bin 0 -> 15861 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/visual_tests/images/shield-on-line-spacing-eq-width-600-400-1.0-agg-reference.png create mode 100644 tests/visual_tests/images/shield-on-line-spacing-eq-width-600-400-2.0-agg-reference.png create mode 100644 tests/visual_tests/images/shield-on-polygon-600-400-1.0-agg-reference.png create mode 100644 tests/visual_tests/images/shield-on-polygon-600-400-2.0-agg-reference.png diff --git a/tests/visual_tests/images/shield-on-line-spacing-eq-width-600-400-1.0-agg-reference.png b/tests/visual_tests/images/shield-on-line-spacing-eq-width-600-400-1.0-agg-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..7eea9485e8762e04925429aed851dc69a1e3017a GIT binary patch literal 3665 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 literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/shield-on-line-spacing-eq-width-600-400-2.0-agg-reference.png b/tests/visual_tests/images/shield-on-line-spacing-eq-width-600-400-2.0-agg-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..627acd9577a1c834a7a901d214002fa2c7871c40 GIT binary patch literal 4090 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 literal 0 HcmV?d00001 diff --git a/tests/visual_tests/images/shield-on-polygon-600-400-2.0-agg-reference.png b/tests/visual_tests/images/shield-on-polygon-600-400-2.0-agg-reference.png new file mode 100644 index 0000000000000000000000000000000000000000..87fa8b77279e28a5c4ff499f27c68598fb07fcab GIT binary patch literal 15861 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 Date: Mon, 20 May 2013 13:02:17 -0700 Subject: [PATCH 041/110] raise cairo visual test diff above 0 --- tests/visual_tests/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/visual_tests/test.py b/tests/visual_tests/test.py index c9601e7cc..b47340b06 100755 --- a/tests/visual_tests/test.py +++ b/tests/visual_tests/test.py @@ -221,7 +221,7 @@ renderers = [ { 'name': 'cairo', 'render': render_cairo, 'compare': lambda actual, reference: compare(actual, reference, alpha=False), - 'threshold': 0, + 'threshold': 1, 'filetype': 'png', 'dir': 'images' }, From 24e9dd7b6c2211eac8ed19324447c78e9aa84342 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 20 May 2013 15:07:20 -0700 Subject: [PATCH 042/110] make visual test failure comparison work with relative image paths --- tests/visual_tests/test.py | 42 ++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/tests/visual_tests/test.py b/tests/visual_tests/test.py index b47340b06..51a35f32a 100755 --- a/tests/visual_tests/test.py +++ b/tests/visual_tests/test.py @@ -4,6 +4,7 @@ import mapnik mapnik.logger.set_severity(mapnik.severity_type.None) +import shutil import sys import os.path from compare import compare, compare_grids @@ -152,29 +153,29 @@ class Reporting: else: print '\x1b[31m✘\x1b[0m (\x1b[34m%s\x1b[0m)' % message - def make_html_item(self,error): + def make_html_item(self,actual,expected,diff): item = ''' - ''' % (error[2],error[2],'%') - item += '
%s
' % (error[3]) + ''' % (expected,expected,'%') + item += '
%s
' % (diff) item += ''' - ''' % (error[1],error[1],'%') - return (error[3],item) + ''' % (actual,actual,'%') + return item def summary(self): if len(self.errors) == 0: print '\nAll %s visual tests passed: \x1b[1;32m✓ \x1b[0m' % self.passed return 0 - html_items = [] + sortable_errors = [] print "\nVisual rendering: %s failed / %s passed" % (len(self.errors), self.passed) for idx, error in enumerate(self.errors): if error[0] == self.OTHER: @@ -184,17 +185,32 @@ class Reporting: continue elif error[0] == self.DIFF: print str(idx+1) + ") \x1b[34m%s different pixels\x1b[0m:\n\t%s (\x1b[31mactual\x1b[0m)\n\t%s (\x1b[32mexpected\x1b[0m)" % (error[3], error[1], error[2]) - html_items.append(self.make_html_item(error)) + sortable_errors.append((error[3],error)) elif error[0] == self.REPLACE: print str(idx+1) + ") \x1b[31mreplaced reference with new version:\x1b[0m %s" % error[2] - if len(html_items): + if len(sortable_errors): + # drop failure results in folder + vdir = os.path.join(visual_output_dir,'visual-test-results') + if not os.path.exists(vdir): + os.makedirs(vdir) html_template = open(os.path.join(dirname,'html_report_template.html'),'r').read() - name = 'visual-tests-comparison.html' - html_out = open(name,'w+') - html_items.sort(reverse=True) - html_body = ''.join([a[1] for a in html_items]) + name = 'comparison.html' + failures_realpath = os.path.join(vdir,name) + html_out = open(failures_realpath,'w+') + sortable_errors.sort(reverse=True) + html_body = '' + for item in sortable_errors: + # copy images into single directory + actual = item[1][1] + expected = item[1][2] + diff = item[0] + actual_new = os.path.join(vdir,os.path.basename(actual)) + shutil.copy(actual,actual_new) + expected_new = os.path.join(vdir,os.path.basename(expected)) + shutil.copy(expected,expected_new) + html_body += self.make_html_item(os.path.relpath(actual_new,vdir),os.path.relpath(expected_new,vdir),diff) html_out.write(html_template.replace('{{RESULTS}}',html_body)) - print 'View failures by opening %s' % name + print 'View failures by opening %s' % failures_realpath return 1 def render_cairo(m, output, scale_factor): From 871ac5b4bb3e1276d92ae031ab293e888e2d654e Mon Sep 17 00:00:00 2001 From: "artem@windows" Date: Tue, 21 May 2013 09:42:55 -0700 Subject: [PATCH 043/110] * add support for unicode (utf16) paths on windows --- plugins/input/shape/dbfile.cpp | 2 ++ plugins/input/shape/shape_datasource.cpp | 17 ++++++++++++++--- plugins/input/shape/shapefile.hpp | 3 +++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/plugins/input/shape/dbfile.cpp b/plugins/input/shape/dbfile.cpp index 7902eb4ab..b6014da2e 100644 --- a/plugins/input/shape/dbfile.cpp +++ b/plugins/input/shape/dbfile.cpp @@ -50,6 +50,8 @@ dbf_file::dbf_file(std::string const& file_name) record_length_(0), #ifdef SHAPE_MEMORY_MAPPED_FILE file_(), +#elif defined(_WINDOWS) + file_(mapnik::utf8_to_utf16(file_name), std::ios::in | std::ios::binary), #else file_(file_name.c_str() ,std::ios::in | std::ios::binary), #endif diff --git a/plugins/input/shape/shape_datasource.cpp b/plugins/input/shape/shape_datasource.cpp index a98121ec8..391d9cb8b 100644 --- a/plugins/input/shape/shape_datasource.cpp +++ b/plugins/input/shape/shape_datasource.cpp @@ -2,7 +2,7 @@ * * This file is part of Mapnik (c++ mapping toolkit) * - * Copyright (C) 2011 Artem Pavlenko + * Copyright (C) 2013 Artem Pavlenko * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -34,6 +34,7 @@ // mapnik #include #include +#include #include #include #include @@ -76,18 +77,28 @@ shape_datasource::shape_datasource(const parameters ¶ms) shape_name_ = *file; boost::algorithm::ireplace_last(shape_name_,".shp",""); - +#ifdef _WINDOWS + if (!boost::filesystem::exists(mapnik::utf8_to_utf16(shape_name_) + L".shp")) +#else if (!boost::filesystem::exists(shape_name_ + ".shp")) +#endif { throw datasource_exception("Shape Plugin: shapefile '" + shape_name_ + ".shp' does not exist"); } - +#ifdef _WINDOWS + if (boost::filesystem::is_directory(mapnik::utf8_to_utf16(shape_name_) + L".shp")) +#else if (boost::filesystem::is_directory(shape_name_ + ".shp")) +#endif { throw datasource_exception("Shape Plugin: shapefile '" + shape_name_ + ".shp' appears to be a directory not a file"); } +#ifdef _WINDOWS + if (!boost::filesystem::exists(mapnik::utf8_to_utf16(shape_name_) + L".dbf")) +#else if (!boost::filesystem::exists(shape_name_ + ".dbf")) +#endif { throw datasource_exception("Shape Plugin: shapefile '" + shape_name_ + ".dbf' does not exist"); } diff --git a/plugins/input/shape/shapefile.hpp b/plugins/input/shape/shapefile.hpp index c082b09d4..9df691119 100644 --- a/plugins/input/shape/shapefile.hpp +++ b/plugins/input/shape/shapefile.hpp @@ -29,6 +29,7 @@ // mapnik #include +#include #include #include #include @@ -149,6 +150,8 @@ public: shape_file(std::string const& file_name) : #ifdef SHAPE_MEMORY_MAPPED_FILE file_() +#elif defined (_WINDOWS) + file_(mapnik::utf8_to_utf16(file_name), std::ios::in | std::ios::binary) #else file_(file_name.c_str(), std::ios::in | std::ios::binary) #endif From dee252eb880d58b8c9136df3b900e8c6d4873995 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 20 May 2013 21:32:32 -0700 Subject: [PATCH 044/110] ignore grids in html error output --- tests/visual_tests/test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/visual_tests/test.py b/tests/visual_tests/test.py index 51a35f32a..e9b170158 100755 --- a/tests/visual_tests/test.py +++ b/tests/visual_tests/test.py @@ -185,7 +185,8 @@ class Reporting: continue elif error[0] == self.DIFF: print str(idx+1) + ") \x1b[34m%s different pixels\x1b[0m:\n\t%s (\x1b[31mactual\x1b[0m)\n\t%s (\x1b[32mexpected\x1b[0m)" % (error[3], error[1], error[2]) - sortable_errors.append((error[3],error)) + if '.png' in error[1]: # ignore grids + sortable_errors.append((error[3],error)) elif error[0] == self.REPLACE: print str(idx+1) + ") \x1b[31mreplaced reference with new version:\x1b[0m %s" % error[2] if len(sortable_errors): From 866320be14526167cb131904e92428b493967608 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 20 May 2013 21:44:43 -0700 Subject: [PATCH 045/110] line clipping drops points so filter osm data to ensure only lines are being used in test --- tests/cpp_tests/data/cases.txt | 2 + ...nes-shield-200-200-1.0-grid-reference.json | 31 ++++--- ...nes-shield-400-400-1.0-grid-reference.json | 51 ++++++----- ...nes-shield-600-600-1.0-grid-reference.json | 63 +++++++------- ...nes-shield-800-800-1.0-grid-reference.json | 79 +++++++++--------- ...lines-shield-200-200-1.0-agg-reference.png | Bin 2382 -> 2155 bytes ...nes-shield-200-200-1.0-cairo-reference.png | Bin 2155 -> 1976 bytes ...lines-shield-200-200-2.0-agg-reference.png | Bin 2992 -> 2912 bytes ...nes-shield-200-200-2.0-cairo-reference.png | Bin 2761 -> 2619 bytes ...lines-shield-400-400-1.0-agg-reference.png | Bin 5741 -> 5716 bytes ...nes-shield-400-400-1.0-cairo-reference.png | Bin 4994 -> 4979 bytes ...lines-shield-400-400-2.0-agg-reference.png | Bin 5463 -> 5378 bytes ...nes-shield-400-400-2.0-cairo-reference.png | Bin 5134 -> 5019 bytes ...lines-shield-600-600-1.0-agg-reference.png | Bin 7036 -> 7008 bytes ...nes-shield-600-600-1.0-cairo-reference.png | Bin 6370 -> 6314 bytes ...lines-shield-600-600-2.0-agg-reference.png | Bin 7853 -> 7741 bytes ...nes-shield-600-600-2.0-cairo-reference.png | Bin 7171 -> 7026 bytes ...lines-shield-800-800-1.0-agg-reference.png | Bin 11368 -> 11299 bytes ...nes-shield-800-800-1.0-cairo-reference.png | Bin 9452 -> 9409 bytes ...lines-shield-800-800-2.0-agg-reference.png | Bin 12371 -> 12254 bytes ...nes-shield-800-800-2.0-cairo-reference.png | Bin 10567 -> 10457 bytes tests/visual_tests/styles/lines-1.xml | 1 + tests/visual_tests/styles/lines-2.xml | 1 + tests/visual_tests/styles/lines-3.xml | 1 + tests/visual_tests/styles/lines-shield.xml | 1 + tests/visual_tests/test.py | 4 +- 26 files changed, 119 insertions(+), 115 deletions(-) diff --git a/tests/cpp_tests/data/cases.txt b/tests/cpp_tests/data/cases.txt index d161516d2..6ba17a96d 100644 --- a/tests/cpp_tests/data/cases.txt +++ b/tests/cpp_tests/data/cases.txt @@ -3,5 +3,7 @@ # SEG_END=0 SEG_MOVETO = 1 SEG_LINETO = 2 SEG_CLOSE = (0x40 | 0x0f) 50,50,150,150;0 0 1,200 200 2;50 50 1,150 150 2 50,50,150,150;50 50 1,150 50 2,150 150 2,50 150 2,50 50 2;50 50 1,150 50 2,150 150 2,50 150 2,50 50 2 +# points are dropped by line clipper +50,50,150,150;75 75 1; # TODO - should the close path be kept after clipping? # 50,50,150,150;50 50 1,150 50 2,150 150 2,50 150 2,50 50 2,0 0 79;50 50 1,150 50 2,150 150 2,50 150 2,50 50 2 diff --git a/tests/visual_tests/grids/lines-shield-200-200-1.0-grid-reference.json b/tests/visual_tests/grids/lines-shield-200-200-1.0-grid-reference.json index cd78fedba..aa56c929e 100644 --- a/tests/visual_tests/grids/lines-shield-200-200-1.0-grid-reference.json +++ b/tests/visual_tests/grids/lines-shield-200-200-1.0-grid-reference.json @@ -4,7 +4,6 @@ "212", "210", "208", - "132", "206", "200", "202", @@ -32,25 +31,25 @@ " !! !! !! !! !! !! ", " ! ! ! ", " ", - " ### ", + " ", " ############################################# ", - " ## ", - " # $ $ ", - " %% $$ $$ $$ $$ $$ ", - " %%$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ", - " %% $ $$ $ $$ $ ", + " ", + " $ $ $ ", + " $$ $$ $$ $$ $$ $$ ", + " $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ", + " $$ $ $$ $ $$ $ ", " ", " ", " ", - " && ", - " ''' &&&&&&&&&& ( (( ", - " '' ' && (( ( ( ", - " ' '' (((( ( ", - " ' ''''' (( (( ", - " ''' ''' ( ( ( ", - " ' '' )) ((( ", - " ' )))))))))) ", - " )) ", + " %% ", + " &&& %%%%%%%%%% ' '' ", + " && & %% '' ' ' ", + " & && '''' ' ", + " & &&&&& '' '' ", + " &&& &&& ' ' ' ", + " & && (( ''' ", + " & (((((((((( ", + " (( ", " ", " ", " ", diff --git a/tests/visual_tests/grids/lines-shield-400-400-1.0-grid-reference.json b/tests/visual_tests/grids/lines-shield-400-400-1.0-grid-reference.json index 56dc7a8e9..cf2b9cb83 100644 --- a/tests/visual_tests/grids/lines-shield-400-400-1.0-grid-reference.json +++ b/tests/visual_tests/grids/lines-shield-400-400-1.0-grid-reference.json @@ -4,7 +4,6 @@ "216", "212", "210", - "132", "208", "240", "206", @@ -62,35 +61,35 @@ " ", " ", " ", - " % & & & & & & ", - " %% && && && && && && && && && && && ", - " %%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ", - " %%% && & && & && & && & && & && ", + " % % % % % % ", + " %% %% %% %% %% %% %% %% %% %% %% %% ", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ", + " % %% % %% % %% % %% % %% % %% ", " ", - " ' ' ' ' ' ' ", - " ' '' ' '' ' '' ' '' ' '' ' '' ", - " '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ", - " ' '' ' '' ' '' ' '' ' '' ' '' ", - " ' ' ' ' ' ' ", + " & & & & & & ", + " & && & && & && & && & && & && ", + " &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ", + " & && & && & && & && & && & && ", + " & & & & & & ", " ", " ", " ", - " (( ( ( )) )))) ) ", - " *** *** ((((((((((((((((((( )))) ))) ", - " ***** *** (( ( )) ) ", - " ** ** ) ))) ", - " ** *** ) ) ", - " ) ", - " * * ", - " * * ) ) ", - " * )) ", - " * * ) )) ", - " ** ** )))) )))) ", - " **** **** + )))) )))) ", - " * * * ** ++ + ) ", - " * * +++++++++++++++++++ ", - " ++ + ", - " + ", + " '' ' ' (( (((( ( ", + " ))) ))) ''''''''''''''''''' (((( ((( ", + " ))))) ))) '' ' (( ( ", + " )) )) ( ((( ", + " )) ))) ( ( ", + " ( ", + " ) ) ", + " ) ) ( ( ", + " ) (( ", + " ) ) ( (( ", + " )) )) (((( (((( ", + " )))) )))) * (((( (((( ", + " ) ) ) )) ** * ( ", + " ) ) ******************* ", + " ** * ", + " * ", " ", " ", " ", diff --git a/tests/visual_tests/grids/lines-shield-600-600-1.0-grid-reference.json b/tests/visual_tests/grids/lines-shield-600-600-1.0-grid-reference.json index db3b627ed..032dfbf53 100644 --- a/tests/visual_tests/grids/lines-shield-600-600-1.0-grid-reference.json +++ b/tests/visual_tests/grids/lines-shield-600-600-1.0-grid-reference.json @@ -5,7 +5,6 @@ "212", "210", "208", - "132", "240", "202", "200", @@ -87,47 +86,47 @@ " ", " ", " ", - " % % % % % % % % ", - " && %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% ", - " &&%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ", - " && % %% % %% % %% % %% % %% % %% % %% % %% % ", + " % % % % % % % % % ", + " %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% ", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ", + " %% % %% % %% % %% % %% % %% % %% % %% % %% % ", " ", " ", " ", " ", - " '' ' ' '' ' ' '' ' ' '' ' ' '' ' ' '' ' ' '' ' ' '' ' ' '' ' ' ", - " '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' ", - " '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ", - " ''' ' ' ''' ' ' ''' ' ' ''' ' ' ''' ' ' ''' ' ' ''' ' ' ''' ' ' ''' ' ' ", + " && & & && & & && & & && & & && & & && & & && & & && & & && & & ", + " && & && & && & && & && & && & && & && & && & ", + " && && && && && && && && && && && && && && && && && && ", + " &&& & & &&& & & &&& & & &&& & & &&& & & &&& & & &&& & & &&& & & &&& & & ", " ", " ", " ", " ", " ", " ", - " ( ( (( ( ", - " ) ** * *** (( ((( ", - " )) ) )) )) **************************** (((( (( ", - " )))) )))) ** ** ** ((( ( ", - " ) ))) ))) * * ( ( ", - " ) ) ", - " ) (( ( ", - " ))) ) (((( (( ", - " ) ) ( ( (( ", - " ) )))) ( (( ", - " ))) )) ( ", - " ) ) ( ", - " ) ( ( ", - " ) ((( ", - " ) ) ( (( ", - " )) ) ) ( (((( ", - " ))))) ) ( ( ( ", - " ))) ) ( ( ( (( ", - " ) ) ))) ) + + ((( ", - " )) ) ++ ++ ++ (( ", - " )))) ++++++++++++++++++++++++++++ ", - " ) ) ++ ++ +++ ", - " + + ", + " ' ' '' ' ", + " ( )) ) ))) '' ''' ", + " (( ( (( (( )))))))))))))))))))))))))))) '''' '' ", + " (((( (((( )) )) )) ''' ' ", + " ( ((( ((( ) ) ' ' ", + " ( ( ", + " ( '' ' ", + " ((( ( '''' '' ", + " ( ( ' ' '' ", + " ( (((( ' '' ", + " ((( (( ' ", + " ( ( ' ", + " ( ' ' ", + " ( ''' ", + " ( ( ' '' ", + " (( ( ( ' '''' ", + " ((((( ( ' ' ' ", + " ((( ( ' ' ' '' ", + " ( ( ((( ( * * ''' ", + " (( ( ** ** ** '' ", + " (((( **************************** ", + " ( ( ** ** *** ", + " * * ", " ", " ", " ", diff --git a/tests/visual_tests/grids/lines-shield-800-800-1.0-grid-reference.json b/tests/visual_tests/grids/lines-shield-800-800-1.0-grid-reference.json index 134943ab2..86da33f64 100644 --- a/tests/visual_tests/grids/lines-shield-800-800-1.0-grid-reference.json +++ b/tests/visual_tests/grids/lines-shield-800-800-1.0-grid-reference.json @@ -4,7 +4,6 @@ "216", "212", "210", - "132", "208", "240", "202", @@ -112,21 +111,21 @@ " ", " ", " ", - " % & & & & & & & & & & & & ", - " %% && && && && && && && && && && && && && && && && && && && && && && && ", - " %%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ", - " %%% && & && & && & && & && & && & && & && & && & && & && & && ", + " % % % % % % % % % % % % ", + " %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% ", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ", + " % %% % %% % %% % %% % %% % %% % %% % %% % %% % %% % %% % %% ", " ", " ", " ", " ", " ", " ", - " ' ' ' ' ' ' ' ' ' ' ' ' ", - " ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ' '' ", - " '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ", - " '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ", - " ' ' ' ' ' ' ' ' ' ' ' ' ", + " & & & & & & & & & & & & ", + " & && & && & && & && & && & && & && & && & && & && & && & && ", + " &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ", + " && && && && && && && && && && && && && && && && && && && && && && && && ", + " & & & & & & & & & & & & ", " ", " ", " ", @@ -135,36 +134,36 @@ " ", " ", " ", - " ( ", - " (((( ( (( ", - " ) *** * * ** * * * ((( ((((( ", - " ) ) ) ) * * * * ** ( ( ((( ", - " )))) )))) ** ** ** ** ** ( ( ( ", - " )) ))) )))) * * (( ( ", - " ) )) ( ((( ", - " ))) (( ((((( ", - " ) )) ( ( ", - " )) )) ( ( ", - " ) )) ( ", - " ) ( ( ", - " ) ", - " (( (( ", - " ) ) (((( (( ", - " ) (( (((( ", - " )))) )) ( (( ", - " )) )))) (( ", - " )) ( ( ", - " ) ( ( ", - " ) ) ( ( ( ( ", - " ) (((( (( ", - " ))) ) ((( ((((( ", - " )) ))) (( ( ( (( ", - " ))) )))) (((((( ( ", - " ) ) )) )) + + + (((( ", - " ))) ) ++ ++ ++ ++ ++ (( ", - " )))) ++++++++++++++++++++++++++++++++++++++ ", - " ) +++ ++ ++ + ++ ", - " + + + ", + " ' ", + " '''' ' '' ", + " ( ))) ) ) )) ) ) ) ''' ''''' ", + " ( ( ( ( ) ) ) ) )) ' ' ''' ", + " (((( (((( )) )) )) )) )) ' ' ' ", + " (( ((( (((( ) ) '' ' ", + " ( (( ' ''' ", + " ((( '' ''''' ", + " ( (( ' ' ", + " (( (( ' ' ", + " ( (( ' ", + " ( ' ' ", + " ( ", + " '' '' ", + " ( ( '''' '' ", + " ( '' '''' ", + " (((( (( ' '' ", + " (( (((( '' ", + " (( ' ' ", + " ( ' ' ", + " ( ( ' ' ' ' ", + " ( '''' '' ", + " ((( ( ''' ''''' ", + " (( ((( '' ' ' '' ", + " ((( (((( '''''' ' ", + " ( ( (( (( * * * '''' ", + " ((( ( ** ** ** ** ** '' ", + " (((( ************************************** ", + " ( *** ** ** * ** ", + " * * * ", " ", " ", " ", diff --git a/tests/visual_tests/images/lines-shield-200-200-1.0-agg-reference.png b/tests/visual_tests/images/lines-shield-200-200-1.0-agg-reference.png index b1201330ac04cd80eb517cdb4454da6498ae4904..0ab97cb12361d3260c28f5a0d80aa7fd71f52965 100644 GIT binary patch delta 2000 zcmbu9S5VW58pVl9Cy`Ks0R$5u5G0}IA}w$wlz_Ada&;phAWD%M;cfu`f&@`Q2_VuE z2~COwX@ag8tVC9t3L+~gND(1K1eN|@@6Oz(ow+af%*|Q*r!)eRU zuc)Y?(ZIU8IsgDc07y#%c|4w=vNGQWg3CV#K>>GR6Rq!3A57;ZF2Pubw97@w5OZ$uNqJOBMv7B8+X&)+r;gi&EA5~}%37Zg{MB!2Fq<>npGF~H8 zI;ge(%1aK*=K@z5put5(FZ8~h(&Tq}+CKYqIT~s7;%tudx^c*o3IlF-2_*jd zPWJXE7`f`kz8|eQJAK`mFG*ortCDzkE6r{_w6ztYI)%cwb) z?v+Doe;u+WRV)q-Hb@v+PNSO-@LJ{*+zSjSlT#GsG1xwVUUhim*C<%6RltGDwll$7 z6Od#{xxfMpWwK@vFHDlJv_V@iY?U#C;sNiG+}4xq0IS@Qh^2NsTFcm#Mm>qK<{+$TsY;L0(z;Vo2lS1ZG*Q!{tGkVSTuz4 zQ02dRx^?}Nk^8NBSeo9~u@rfSGU#9Zrf;MDX-=JLk1wzW!ta=)f?Ay`(;U($e@w(j zs9GGvv;jFoaZs}}x`^l;&ZhJ?Y z>#bI-hL@IKF5FvgE_5h)Yu9!w!R|*1UwKPPIC^P68cfKEXWw`cpYnYjn^>@`)^vpV&V_01*H+`gj&q>);hCZ5ASSx(cm%HX= z?qH-9tItMN(Wfknb&mS_*ew$_4quZvHRG&Mclp?l*R1GmvufX0OlM3RY|giYwg*Svl#bAr5|+wnRK z!-0#sUaN1ot}I*FZmzae2=|2H`q;gkKu>v+m5&tqfTD_0T zT-Z`$`4Dd!M@4d-I-|ehzRYWd$lUdaNm&d2e6@@nSd)7*L$=%0>1MqxE5E0e`sPv`Zlej!Bu!V97xkZR8;=_qFOVR;?;L7p z&(G5Lpc`jR`%W+NGFzmQEuMK!Q*%j{nd5Q8gB&}xSe{PQ;(XklbI})7=Y^^xM}YB1 zKFf|R{=(qaw$Uf#pvEfSZ|6L79kRDZM%q8uA3tk@L_BpJ@3@a-{Vr<^QfzX1`ATlV z{Bs@t3ZdZ?cYAsWmrd29tqs?vK1T+cD+<1I8cG37UmFqW`_6vzkEz|~dPA9JvVHN@ z=@psBO%EQfnOpR4O_}caCU)O52xytuJ@8!vHDhM4t2kQZQ4%3|r0?=0I_Bm?hi6dw zo$y|I&%Lmghs?eE_2k$7=ur|f`VFzfWCost2+=5Okz zDO8t9%e?(-vGb!3g6U~BlA;rc+rN-pEF>?C;!bsByg&I4A=>+OqdlV!O#ynb&bI(2JG6mlX=g1qyIM}%0U~Fth zU@$lg#>vSEhr>}Y7(YM1kdTlP7oV7z*hwB9bv{0MK5--xd0aw5Mn*NM*LqJZjnp&9S8oS6GCjjyC`uO+=2tb1h3XqVHhzNv0K!JgQ zh1S+cBqSpP0RR*p9)4&N2n1DC=s;aPE-p?>3xc$?I<#XF5)$~4-He}_}n&~=K({3EEWq|T4FLG z=&*pGuZq?yY;4>Gmgc4|MC#W&`+g(3;vLQgvkwSLCqH#XUuAzO(Bq#p8#OJ;$sN`~ z6#3B2Wfs%>Gz7W7tYIr%*K9du`sCv6b&ZA2yazkO^ee@p@-xB?O$L$9kwU@$B=}G1 ze=E~?N*I01LNbH;>QZ(!#Ui{DSIWSZZt$kFO6s#Xj+K*+L|1b$aAX`!E<=it=zZF; z9}yDnE?`+2Ibf~~aATEj=M}eHQnYG4+ufB6Qr`s;Q_tfo&m=HEy7qIXAsuz*c3mgxeI{<1A>aYLOTpJmizuU>im?lXZH%9a=gRig95t?u+h zvWli6Ex+g*|6}(CgGDohCqz>qZ(p>HRBm`yNj}x!cWp8BtS7MPn`#h!cg3EfEL(Yc zq9?TcXe*)MzA;W9MX<(fYIC@w#=AT?Dws@?)u}w^>-D~%O60}UvtF{7hruV$>2+xL z6IW4$<1wWtyg2?gg>n6PNrS6D-wNw^>{Sh?$?TcIU9%)IrW;Y4zs|%!+CO6uHw)7r z+VFrw@tObW_Y&0;m5s>qx>g_LUSmgBn%kH?s}){h!P`oRz28;jgLxIJs(x@dN%>Kx z(jBjURz%2HiqA7I(DA~s1$WqR@FW(ef4D;Uv~1t#@ibullAsx7GL2+%gJjZqg|Z%} zFu57!yD&wYuW*R;SkT3p(?%;Bm!rJpPrOkEvZu4w?Q{+4_^#|cgy(Slo=nJwP2r0^ zr}TFiPfWerL=+`|&~vGyU-gGZAl#sHy>TfvoJ4)ar3emJ!&9vfoL9|)Q$=f1-q`1p z1qNP?U5$cbx@pZ}_6t339qp8NHr#VR^y?YCB}Q^FW%^CCFZdP*?6ig6w>T*p>U8fh zl|955^RXu%`7W?_KTGq1E{MvFlAk{{Urte}MT2|lr|Zvq{3%=otRAGI!Th`~20ux~ zXd-FxC#YHAZ#Vwij7a-lQ4!!F^r2I2TRYy{NI$I7okz5o{@gdMc4kFKtdFbPoldD| zebQuFpn<-0YRUM%*i7>GfJW7{J3Hi;$nbMR4 zXx3|$hojn*+D{@F`%V|)n10Cf-)C>3&Rgy7f})!NLKz3P8I?!vG;=97rM|6E@e*qq zN&uS2?a50X?Fo2eXhDw2-9}q3NKO-IJ6D(>`B3l9&Y%Rs*!;qzWb7^aN9i75zQwe( z>+%+}btG)`!6*^Da%UstW@-X$@pHqmRbxqA;Z{GY>E`u3sK~*Db7qqmf^a{^2m;iJqTHXKkusws&3h)ZBtV1-@xh4qBbHUISm2Punc3nlb*E(3t&n#Xc%p zsPE&(7s_YMpwn_l{fH$x=8PrdmAT)GwNnN_jk@)~$5D;}+*QfATO4@HZmBO;8S~98 zAy(oiA~Q9qWBlr|*ucbp1*kNT-Et`%Ex!|G(AhINkq-FyRf`I%VezRPEq8M;G%nAZ zbl{p&l%6XpFDL1^|BY2+e0b1`vFeE3H{S1({MQ!BYb32l8UUzzPFOa4ecI-+ccAM|Jb@c||_Bgi|% zct6$*-?+7H%dFZ-MhD0Gt_@XCmu8;~dcf`l2Pj<#!u4(%>}J284Tv&KQXTcc)btp( z2jmdEP_ok0RX;irkgQTleO#6r)`uk{?0lrRxW~JcM+)r&BYe;YAYsz4!QtO>FQOR+ z1$Hh_v8cM7L9<;be=C|ClD8p|?elF7QO5sl^ktB|TU`9AZd+grS^u@HmrkVDkqT{}JcG;+yifDqVq4wnJFr?96Mh{@4EkhKnCO diff --git a/tests/visual_tests/images/lines-shield-200-200-1.0-cairo-reference.png b/tests/visual_tests/images/lines-shield-200-200-1.0-cairo-reference.png index 1627c6f0e0a4100b17b27c34787f56401744f6f8..2db6719ec54859fb4e1530b67ec60577090c6bb8 100644 GIT binary patch delta 1686 zcmV;H25I^05V#MJBmp&%B_EMTIZ$3+UJefb(k3SU5fKv;{}mPgWMpKPOG_FW{~aCw zA|n58Zf+?l|1B;5b8~b4E-rR=Ntn*aa?K1oDDRCt{2n`>LzNECn*B3^5})@_TnY6WFQDDKL(YDKPsNL7f-stbz> z{QsY@lcWloxg?Z4J)!6QLd?lb@GkRbi+0~i#O-T^L?KL`|mhEb%J@g5g6 zk;bX_xS@%78x(;Cia_HR5Gl5J?Z8FZBHMLdhv1?Nk@&jN|NHVBn%XA~};kO=hG?b0PN(U{M<)qtZC1Lnu+I z$TQ;dNA3(5xF|zJQg{nc~_Nf(7N-=h!YJj+YjZCXCPN9lVyPViaHSBHcx=2Ny>GQ-Y zCsr~y?Lu(3D&E$yNFy8N_uKCBpUJW`XD;-F#5<~`Ru8^^zxG)Uhs)vyPDGm7;8x-O z<{(sHZ}wFzvMR;0^N}CblKfHX?Fj*h>BHgi`*BsHnQdcG*TNm8(*qPTwdT5 zs%U(^>r|sJ*41`%)NQ-i6)CSA^_)_T)S68dP1%gG3sqFM>z%0O^lfZ(uJXKAc0?{o z524!gbXC%C5|mOy7*#B7h$IeLJ{&65s|G;OAn_FW6($#>qhn-h#T|}5hIZYa;$5M-blo2%I+fVXR1o5dAQNE^kDLqtt z>qJDHMmZI!$P?5)C&rV!6v4(dGCh@4X+13a-nML{QLRhyE)4F@OlGBdA%anw92^g}cEh7--5uV3Rh2?)a8? zDT0ktD=yEx=;87YgP30$GZu=Bmi?fr+nEAo1l4@6le`o`ETSn(pBAY-EXv0Y(BO^Q5ewYYbAJ0h)C=QQ#1Ig0$&Hbh9uNksanYt;_cPQLH5nG0>5+>096d_rtz zR8pQ%*faaZuOl7xyPa9VdpF delta 1860 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 LNkvXXu0mjf1$%SZ delta 2476 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 K8$uP{BlbUXYl5}_ delta 2503 zcmV;&2{`t<6v-8kBp{nmOjJbx000C81QGxM82|tc1quoZ3KRte9smFk1`LrzMFGu` ziX(rc09>vBQOp1{{{R362LD-ESr!NI2?_rV4gX$VUKS7d{}B;mV`Cp5>=qXPQ&WbW zFJT!O|7d7vBqZ@39{gNu&|1XlXw9r zf5XH7|Hj7L+}zB}|Nqg^)YSjx=H}bm|Nq?F?(XjB=>Pxi?f?J(2QR74000Q-NklDoAw)DhXtk z?mUepfy@;(`ndgqu34*X^iC!&EuHR0gO-cdvG>^lxivj~U+HjqdioK16fV6+kiQ9e z^+~Jc_9buFFa&GV&LJr%G#hn*- zL1dHs`lSqu!=V=k0+akAA-`l8iz_d3i^3*3)8cUFRX|oJRo(b`o%XX1KUWX@F_|uR z-f|L1dEHL`{Ycdjh|JsSkqK<-C&2Km>1EUvs5kVmbA{2C3$8*4i`^I|~Q1fPaIkJwn- z$(a`c@(&@ckDx=bv9^*suNed;ctYG4{As&D9D2wxiVmiO!E`bncw4Rj+OIE+m1R%bv=6YMMy`>6q8({CQog zUfbwhO#+E@OHkjcmlH3MIWB40nB$6@WrtbPXHL9C=D1>iOH+<3ZjL+5lsSR zQgU3-3>{`opE>bz5I&1hl$PUy=69l6=Jc5puegrDEasxr92aYTE?3K(K6B!YFNl!i zI*~qu(41v|aX9qGATW!2({o(V{K_WMXAZn_i(<%e?SRyq%mnQt>-Uw>dhKH!o=^|y z_N4B-%^*jJ7Nj`^h9As!dKD1y10L+L<(T#`&(N%uBX0}>Ij)20IWA~EzKQgi1Fry? zAfz>yOU-ey=DoRxnbT)Zyc~o-F^;0N92YbniE5dD(`Qb+90YP)u-8h-aX~Zed9tR@ zoOl65q#~FlImgADp~I}{GbdhBPep(wDaR$6`F1*E`pk)!)Kd}Ml91yP&Ef_pTl&n2 zm&hDfEjn8&mA*dN*7jvd)y1~9li!v~XQ4HrS*z{$Dj+h)RnuN?eU$e7GM_Mi z>Mn}CR@v%WeX`dcugQZWbV_T&f%MY(rekBux8k={OSC9|9gc3xUl(bQ)`bEoH|Bb+ z9-`&O|F^XoXvAItsdZEBr5FWLqVgK}Ai7#V+DjLbKfYeyMJt$()m5Q|`uR`=8P~4> z49H?5_A1B}9l;FD7Gz2JQ&&L@D*yw3GC=FER!~7Iv=ehFu?1mg$R$}DBtf7ysG`3n zVnF&DYe)rQE3Wwz&@5|^NjfT12{8z-4H{#IHNt?*ur(BiNP_V4siDS9B`LN@5OlQ` zn`xO!5ForZsG8UB&@dp?e4!eLCAiOqcQu)q*VO9{n z(GejMAfkL~R6o};sshBOT5A@@%)`Jzcx_O%({TMofxt#bgh+t!5V5(|ZyI8Y0I{jo zs^(#;p*9FWL~VhThs>4(O!cG2eJVDh&LBpjYBj=`^`SE zbVMuG)mrQ>v^Z6T+Mw$!1b5gSw((4l+{PE_Xhpoa;A&hxRiv1w8$7Bj_SIVaE_9TI z;Q%40*d2DKaT9?Anab1r$a4M719tsP-xWAzZ_6M1}iLEQL0?F{!1F#&;RILS3 zV^?-;U6Bn)hPNK@sNTs`tp&9~u-8J?6p^T0 zAa*ZGkR?mB59G2gKS=u zAhq=%QV*JTiAGr%KcG{zdq6b%_6_nI#6q?pP5XAE`~>k=lRyGD8SNU7{{e)Ci6nb6 R4EX>6002ovPDHLkV1hbdyo>+< diff --git a/tests/visual_tests/images/lines-shield-400-400-1.0-agg-reference.png b/tests/visual_tests/images/lines-shield-400-400-1.0-agg-reference.png index 4ce5cac8443de08b4fa539ad5d8d8da1e75332c9..f1be4a8932848662b888d7740bd1ee091b53f0b6 100644 GIT binary patch delta 4647 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 delta 4674 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 P4OI~LbTrDK*1`V=(=WcQ delta 4100 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 I0ecYfKZu6aGXMYp diff --git a/tests/visual_tests/images/lines-shield-400-400-2.0-agg-reference.png b/tests/visual_tests/images/lines-shield-400-400-2.0-agg-reference.png index 197cd2f636c7f89170f730b996075dbc1e53ca86..8133c8197972039e7be7903443a682afd95596e4 100644 GIT binary patch literal 5378 zcmds5XH=70vksz25|9v@5ZWt{&{3qwK|qp7mtH~;Aq!X01K5%RBFW_A@hU9-Eu$b3%k5AP|Vt z&;V-*0Ypt-e0+RpriF`(l@%>0C}{P^Xmbt@ml8mTii#RR zAOHaPfIvtI2_zEv00feil~qtshy{U^m6h+qU|ArLnwpxXrsi7^=&~BxKwBG&#dfo> zRKQ@gKT`olMn)#aCarMz0tjSkY6=3aG_%W;!!)d{n4e;t1}#W{#>pV33DB8EkN^V& zx(Xt%9OFB2f2T`kI=w){J6Thx+k%Pk(%0BRh6F3Xl!g`kjX_wjE;_uk>bxiJv|dO!!zye zjJmpp2FB3PP-iED(bYB3y*oZW{`oV5PF)%vX3Wjajg2uFV`FqWeQ}Y&_#=$-ek3di z1Ya`5>RR8=`jNeVjjvNMHdcsS|9prxZW2G>@{2~4w)LKHYe=JI6soA^xrtvguuwE4 zNC%!d#~!UWbYm`XGxtaDN=}=o{k?_kY^}!F{+-`1Xq>CPPrVi~bK*w2ec3&L7C*j?HF-`Jaa~AbBFuHcyCP%$u z`*uuT@;AmAT8R9Q7q4wfjam^P$|rYkwDni_l4N(?C)lzL+_Z0nPk8RGvHK8PJkILX z6vhJ+HpU1+)U!y?U?s3Ew%~8!p{#mk!0fH%DuDJ?#ic!iTWNNnOmD5#P;}Moi*$%fLgcKo#(Q$;`2@^->vlRteA4_6z2M zZnqa=_|G8RCN^y8b}-*#P4wB985KtMKs*kzhQ`Yw`*KU3n@2X961)=iE3!vAFng3Nq3`aL^q`|_6F0^Op@(rTeOOJ-pWH_D zj}+%l`AF!M2}%O{o2frtK6?sKxF!CLjB#6XyIwr5?NSsmk2~$;PaU3VakuPFc=Yjv z+nzpgeuNKWL1$XC8Yt~`(=X=YcE7#e%Pg#Tw7mwb zwM>}Z)%H}l23o(f!ypDDBBJ(ygPc{9S4VU?D_?folCU63jJu@aixGfx2%O|oY*uVn zpM{&79^>zaX}vh}>Q_80cJV}B)^R`4IQovXHa*hhm>Nzk%v$fJeZB@adSTSYz?3zXNSurLaHDZ|^$2HIQbN z_ENP)dptULc7SwnAh~gN+b1Ltd(bP+$$U&7$E8thP@qJ2lGc>|RY<-2aU0s;5lqB2 zGBP~vLqgzm9@K&}P^0YZMd447RGv6$gneC)`-h2IN5dCcs^ZWA&OlGw4(FcoeDhC2 z<}oH}YB{(>>kDHeBiIYKxaaa8J`U{degRzz--kRJk)EF`OH@@tX{gl;Z;VjqaP4RV z2aTFDv)$|(%C>|64H-1N2Vl3iNi2X}HRX#Lc{pOt9?N59VuF=T&wTzi0k=b$M;keE zoFKijI@hb7uJ0#$a(s&4jXq&1g^Ir5D*6OBW6*s*AF~=)C?`nHdG!#7kw!=1U&+2u zb!msY8Q!uFJ+E(L$WzXYaKfX^@JFbEQ`tX?Xc1JSkl34*0vg;nWNi&J{ zR1Yv6Y=!x9Y-Kk15&9vNwn8r<3er606<)gi_&EsWLp!863+|*5xBGY5Ou`d6Ln|V8 zCNCY^)l-%HOhHH7O)hd2XK<{=h5jLV6*KC^GFMWCI>uPd;BCF7^+G)zWe;?DRo_WskAdpmhI&w;v$OrU4d;fa^9$`m|Drq^dCVfjx z5T8wpyxXJ`rTO1!TJH+9KH~TKYziNMd8RZbuQhY|3KT5R7L8sBCeLG;yAC=3RT|UL{EKIW7 zn=uut6}JNlRK`R#_4^6>0SD_-(o-c`!P|Nj2XMsP^pd7dsBPh^mZDpf>9+^5_cZF) z)_JOMi>Zo#LF25}`)i{nDrQ_)vPQm?))nlDhkm#Av)-N>HhCvs=q39z*HXTVac}bI zgIr1qaoW21VCCja%NKvW`SEJo)!4Y~fUG0O)WedPl_X~kJ_Jdt=*@$L&f$oH?!cLH zVjNMt@05N7>jrC8L6UYiS-Iv;(5L=QL{spPn3c2LdVevZ;=KFSpE1X|5+9})`IBPX z)|V@*pFQQvh`#UMJdtQy7{B{vIqB18nflbc=K-pLUzzWHZs!5&P+9rgqGx{ZbC{Ay zbA#saaM`7<_+afr{Z6eKjs;qc)L4TsR3?YCy5JjeMK-Y~I90UhR*TQVqS9fhnx;cK z5Bt&@N6uHSCbi$qb_bfZckAJSKc2itK_)LvC-Fg*b2&9jtN`I)HX@*j>PwT$qW9)CX4_$;o>fhOj315*YC0hQZq5rbUxYCTmH%0LP zx~;NifqDLIUe2++%ALr zsbdbCB;&}Qrfm1Z;RWwtLx^a|q#tI2J z%)Y}(&jw;J^Vi(o^UQe+hK?~lNxRB;XdY4*j12A%YLQmZQR!lqU5!T7OJL*43z`}F z7{CX#9T!w$as8~cP%z+s8-;e_V{V~$;!}kFqC%I zRffrt)v2dD6aJZZLYx{si}R^;#!zF2-^LYcTINCxOda$6i-5m}lH@}qHQR48L?(q+ zC^0oAC9lpT;pl3l6pt-=puZ>6tEUp)davqx!lZQW5ZGeJaMNggSUdfV^H=w0mBQg% z0}D?@R3quuhQ<*4*GJ8A0{Gzk@3a1T`g!`z}~f`gFWXm37~ z@1J{h%y>uQpz+FK#8i^A1C@S=nNA8$h z&%0J2%oS4b(olqdc}k$8(YME;t47X3J59{>i@<=~$DL>shgFn$F>RwWsW40b zLiqV^^%t~2v%(mEm(KU&z5gD;%CESjdBeXse z)w;8{v+un*xUAwBz|&NLM23kpwFY@t3$Y;9%+z(f!m#-fKeVoo?X1iT2AUUseS>O^ zvALyKVeJ&~8IjA{fC*`5d@?q6@QhNzf2j@cH;v{1uT8II%g8LwC$QVqEekwdluA>O zTr?M5D{7))S{7YpOPY6{M0VD$evN@`UD_SF^%Z^JKz-@X1BJgZr6ws6K?VS;vo5my z<0d<`{zA!K6+!=>)Y|}dYky}?L1DgvQu1JNS9?sUnksQI+ZmF+w%5!(y)~q15Zp{( zBhHDWO1N}ifW3S-5PDc)ouQif`vtru_9&@#Ld%A%a;y-4yFWAzX=uE)mB=Q6=dm@E zdq>QhY93~^_^N0B9^ehi-UL%nceX+-|H{@rZKZV4z|29`RaFU(d5e;nA{l!yYclKY zy`8b9bJcaXe!DwDO72)7%j44N{ZGGswa+G}5<^xGQr=Zpf2?aX#m#wNlJ!oop!dp4 zuIxNRe`vBGr;EvQzu_ejHNL*RULEnbY~F%r4Wf6HDzUuNy;0>cIGUzi^E$Z>}TYU9wY*she8P4GTf!@DH4QV*w$XVl?|n-kd9x;*o)z@-9d)DxeN z4(qLrQNzWIEaYaLPK*_B9(kuct~|$Kbly#NDgE1J9bUJucDgR_ua%RWd`aIAe%beX zfDM&jM~@F5{s^V{LuS?uH`)`3mBD9V$vq~6MFR-+_Ht6#h1a%=+g{i3^m`|$mBT69 z-{f*QCl|7NBe+6QZ(yTKE!&GV$!0gl&E^5^##byI`8fMUOQVp5i~gUIwg%i zIkHuDZRi7MM=?vyv9O_mP}^Z&j+EhkI$wk0v+(76FQTobTuGmOr5+*7bS^l=PLyW` zHOq(gZEX~Y1ZB|*v0>lM?BrZY?EZ~W&McE+?`j??g!l==>H;tn*o1B;pls8$WM-%2 zN*aLvL!|-!aeZFC2`)2uQc^?cNfEDCwE^FHe2`_z=MP_t3UjZIhRsiQf8g|8$(w9hRv~&VcX-n2)m^}+F4}l6?mJsZe+g584-#~4YFr@# zs)`;4S75f;areF)7qlEhM;RJ4>`(Y)h+}*l4bJ*1pU$gFM|Kf2_~HJmaE|YzCOjSn z3Q(E83=Wh=S3dR4r`#@<8u|P;Vm97FpSKf;Z+z?x-g?~#c{#q`dTd|HzIdpL2)D_o z$BF!_go|gEbmw~bsA+!30R-g#W83Bb&|Es=x`9_%(%Xnr`_q6lyljenjdqFpFJK#= A_y7O^ literal 5463 zcmd^D=U0Nf0th~U^dcRk8YA6Mq=eoPM3Ev$5l{#cq&Jaf0Th%by^Hh^ zM9Ps6B*LM0DGDepZ#eIL*Sa6>{Ri&-Fl+7Kl)YzWKhK_76K8_fWdd`9K_C#5z8=a9 z1foefC3;#ar@^`RgbKcz7@1$E!gEvtLBKR;m_Z;qIy!c$2*eIK&j1F4nHd-W5Qvu+ z%*@Qp&d4MM0*RbqV1>aHPffHqI5?104hst{EfRr1+&pDk$g;9t;^g7u<1+w*0RXrS z0tpKW3JVK|fk5Ko;!;vlu^^C~oLn#zng;?YDJiL`sWpN?*OicZnwlsSiaBY*SsyA;^kD_pamIOiMLXh2ekiasClL7+Sg9_Kr zaM00F#u%ig&WYIC+J0k@hCnEFa&leF1`GWBU)WIW?3CM1PEX9tIyfyToSg3N?mRpc zFE1|upil$^e0+RFL@4!!XdSUxX=zGeVBlv%Lj?s2MM2@=!-qwlo@#0oQ>~3epI8)% zg2iGfy1GV2l=%2~42D9nwe1cG$;ilfla%D{PN{uRNZmI-KcC{~S6EmW9895vgp`(+ z#>P?xam|#txSE=pfus&fMn+$Cb#^wTwY8N}SXf+4>FMbqy&E1F82DZ{KG)q%X=rF} zrhNVSwXcst!Q)2=zsY3s@Gxa%#ZBvBi0 z;OIC_@VF!&9^QouO*M1G(vVU{6OkAl{0(JGJ zXH40Y0%KLqbFejO@b;JIyEOn&2yc$}>qY;bzFa>vTSZ&N%>|xG{IJ*D-rK&T@SIM- zl1(0Hh5c~&I}`J(4zOgA2kH_62l$NUraY-G3l-%mcW%8gjP|;B#%1?1On}E3)Bn`@ zCBj8f_7wc6n^$o?KYp4OZM|XopMqkmh!!k069>p=N#lHuIP+>VD_gZ>%y<=-sIDX& ziR(eqS79K4)$!rtRoqxN5?`X_&4}RBPRTg<^bMS%Di%P=xtM zS2YhBNF&_T=FhKVsoa!Uu_9{wbM02j#bEs9m{{9zgybc|cK(HtL557$I2 zPDh3RzSx83N-o{2&O-BzyrUOd@2lP+;b5h)anK3+fXYiuJhCo|v`pBuiI*v}CpUZ1 z05PQOamp*05A)p_CeyHNboXkn%PZb03_Spc0NpMCHT6pO|A$9`h~z6G88I(p^jhL? zQr^1@5lqhj-Gs+bmj!M5D*w;Lg7@!}apo%I5$BrT%LK8Q5D0_AlNb)P>>fGI4<=fy zu!xKr@3|VYRw=tz=f@zb`?yB~#au!!-r7m>`gz7A=}c~Jvu8$ir~cuSM1KP(L6ic4 zKP1K_=FH?jc$YN5vta7*dct2)R2R~+3AhLo#xj_A7P?(TsH!YxN_R5Y;un;66?kY~ z^Kxt>;jX_c9w~=}hqWj2{syGaEtCw5aHJ(>Zyt)Ydvd{XeF6759aUKjJ⪚XV2rUH(6AK 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 Di&m^@ diff --git a/tests/visual_tests/images/lines-shield-400-400-2.0-cairo-reference.png b/tests/visual_tests/images/lines-shield-400-400-2.0-cairo-reference.png index 2018347d899acb65151645bcf9dc5f535c3f5290..8189a805b9569f3ab9f31de1f0da842a0f74c65b 100644 GIT binary patch literal 5019 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 literal 5134 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 Z&fD?pS>O6)&ir;1rbc%S%Wgk<{(qO-Orih) delta 5545 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 diff --git a/tests/visual_tests/images/lines-shield-600-600-1.0-cairo-reference.png b/tests/visual_tests/images/lines-shield-600-600-1.0-cairo-reference.png index a4fa0cc70d4b40b3319fa42bc22a0a6b757daaaa..20ba67d4e461ee3244b9d9f1be632fd09fce88ef 100644 GIT binary patch delta 4996 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 diff --git a/tests/visual_tests/images/lines-shield-800-800-1.0-agg-reference.png b/tests/visual_tests/images/lines-shield-800-800-1.0-agg-reference.png index 05d3ca6ecec7954d9ae153771fe834c6a95efac9..0789211a79ad2b189e83fa38a323797233f4bf9d 100644 GIT binary patch delta 8591 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 KtM5Pm@BaY4_uZlZ delta 8730 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 literal 9452 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 diff --git a/tests/visual_tests/images/lines-shield-800-800-2.0-agg-reference.png b/tests/visual_tests/images/lines-shield-800-800-2.0-agg-reference.png index 489f43df6007eeb8932beda5ed77216cd51fbae7..7c433d0ad017510a7238c406792b483e14737cd8 100644 GIT binary patch delta 9882 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 delta 9973 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 Z^olW4x0=NtcAWu!TI%|D3T{1m{(oeU-4Xx* diff --git a/tests/visual_tests/images/lines-shield-800-800-2.0-cairo-reference.png b/tests/visual_tests/images/lines-shield-800-800-2.0-cairo-reference.png index dbec8e0de840fd0ab6217cc7be01df3b2fc39e41..fed3f6511e3a553e17a5f59d42e181b307cb3db0 100644 GIT binary patch literal 10457 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 + [mapnik::geometry_type]=linestring [name] diff --git a/tests/visual_tests/styles/lines-2.xml b/tests/visual_tests/styles/lines-2.xml index 2f541ff43..bf4cbf22d 100644 --- a/tests/visual_tests/styles/lines-2.xml +++ b/tests/visual_tests/styles/lines-2.xml @@ -12,6 +12,7 @@ diff --git a/tests/visual_tests/test.py b/tests/visual_tests/test.py index b073e9762..f1ae4866f 100755 --- a/tests/visual_tests/test.py +++ b/tests/visual_tests/test.py @@ -319,22 +319,19 @@ if __name__ == "__main__": if not os.path.exists(visual_output_dir): os.makedirs(visual_output_dir) - if 'osm' in mapnik.DatasourceCache.plugin_names(): - reporting = Reporting(quiet, overwrite_failures) - for filename in files: - config = dict(defaults) - config.update(files[filename]) - for size in config['sizes']: - for scale_factor in config['scales']: - m = render(filename, - config, - size[0], - size[1], - config.get('bbox'), - scale_factor, - reporting) - mapnik.save_map(m, os.path.join(dirname, 'xml_output', "%s-out.xml" % filename)) + reporting = Reporting(quiet, overwrite_failures) + for filename in files: + config = dict(defaults) + config.update(files[filename]) + for size in config['sizes']: + for scale_factor in config['scales']: + m = render(filename, + config, + size[0], + size[1], + config.get('bbox'), + scale_factor, + reporting) + mapnik.save_map(m, os.path.join(dirname, 'xml_output', "%s-out.xml" % filename)) - sys.exit(reporting.summary()) - else: - print "OSM plugin required" + sys.exit(reporting.summary()) From b2fc3d7d577f5a43da92b6a5ff454315f7d49ff5 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 20 May 2013 22:27:18 -0700 Subject: [PATCH 049/110] update expected grid outputs --- ...sionformat-500-100-1.0-grid-reference.json | 20 +++++++------- ...rmatting-1-500-100-1.0-grid-reference.json | 20 +++++++------- ...rmatting-2-500-100-1.0-grid-reference.json | 20 +++++++------- ...rmatting-3-500-100-1.0-grid-reference.json | 20 +++++++------- ...rmatting-4-500-100-1.0-grid-reference.json | 20 +++++++------- ...align-auto-200-200-1.0-grid-reference.json | 2 +- ...ine-offset-900-250-1.0-grid-reference.json | 22 ++++++++-------- .../lines-1-200-200-1.0-grid-reference.json | 18 ++++++------- .../lines-1-400-400-1.0-grid-reference.json | 18 ++++++------- .../lines-1-600-600-1.0-grid-reference.json | 18 ++++++------- .../lines-1-800-800-1.0-grid-reference.json | 18 ++++++------- .../lines-2-200-200-1.0-grid-reference.json | 18 ++++++------- .../lines-2-400-400-1.0-grid-reference.json | 18 ++++++------- .../lines-2-600-600-1.0-grid-reference.json | 18 ++++++------- .../lines-2-800-800-1.0-grid-reference.json | 18 ++++++------- .../lines-3-200-200-1.0-grid-reference.json | 18 ++++++------- .../lines-3-400-400-1.0-grid-reference.json | 18 ++++++------- .../lines-3-600-600-1.0-grid-reference.json | 18 ++++++------- .../lines-3-800-800-1.0-grid-reference.json | 20 +++++++------- ...nes-shield-200-200-1.0-grid-reference.json | 14 +++++----- ...nes-shield-400-400-1.0-grid-reference.json | 26 +++++++++---------- ...nes-shield-600-600-1.0-grid-reference.json | 18 ++++++------- ...nes-shield-800-800-1.0-grid-reference.json | 24 ++++++++--------- .../list-100-100-1.0-grid-reference.json | 20 +++++++------- .../list-150-100-1.0-grid-reference.json | 20 +++++++------- .../list-250-100-1.0-grid-reference.json | 20 +++++++------- .../list-300-100-1.0-grid-reference.json | 20 +++++++------- .../list-400-100-1.0-grid-reference.json | 20 +++++++------- .../list-600-100-1.0-grid-reference.json | 20 +++++++------- .../list-800-100-1.0-grid-reference.json | 20 +++++++------- .../rtl-point-200-200-1.0-grid-reference.json | 2 +- ...mbolizer-1-490-100-1.0-grid-reference.json | 20 +++++++------- ...mbolizer-1-495-100-1.0-grid-reference.json | 20 +++++++------- ...mbolizer-1-497-100-1.0-grid-reference.json | 20 +++++++------- ...mbolizer-1-498-100-1.0-grid-reference.json | 20 +++++++------- ...mbolizer-1-499-100-1.0-grid-reference.json | 20 +++++++------- ...mbolizer-1-500-100-1.0-grid-reference.json | 20 +++++++------- ...mbolizer-1-501-100-1.0-grid-reference.json | 20 +++++++------- ...mbolizer-1-502-100-1.0-grid-reference.json | 20 +++++++------- ...mbolizer-1-505-100-1.0-grid-reference.json | 20 +++++++------- ...mbolizer-1-510-100-1.0-grid-reference.json | 20 +++++++------- .../simple-100-100-1.0-grid-reference.json | 16 ++++++------ .../simple-150-100-1.0-grid-reference.json | 16 ++++++------ .../simple-250-100-1.0-grid-reference.json | 20 +++++++------- .../simple-300-100-1.0-grid-reference.json | 20 +++++++------- .../simple-400-100-1.0-grid-reference.json | 20 +++++++------- .../simple-600-100-1.0-grid-reference.json | 20 +++++++------- .../simple-800-100-1.0-grid-reference.json | 20 +++++++------- .../simple-E-500-100-1.0-grid-reference.json | 2 +- .../simple-N-500-100-1.0-grid-reference.json | 2 +- .../simple-NE-500-100-1.0-grid-reference.json | 2 +- .../simple-NW-500-100-1.0-grid-reference.json | 2 +- .../simple-S-500-100-1.0-grid-reference.json | 2 +- .../simple-SE-500-100-1.0-grid-reference.json | 2 +- .../simple-SW-500-100-1.0-grid-reference.json | 2 +- .../simple-W-500-100-1.0-grid-reference.json | 2 +- tests/visual_tests/styles/simple.xml | 4 +-- tests/visual_tests/test.py | 2 +- 58 files changed, 460 insertions(+), 460 deletions(-) diff --git a/tests/visual_tests/grids/expressionformat-500-100-1.0-grid-reference.json b/tests/visual_tests/grids/expressionformat-500-100-1.0-grid-reference.json index dfe52da9e..c5fce6529 100644 --- a/tests/visual_tests/grids/expressionformat-500-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/expressionformat-500-100-1.0-grid-reference.json @@ -1,16 +1,16 @@ { "keys": [ "", - "-1", - "-2", - "-3", - "-4", - "-5", - "-6", - "-7", - "-8", - "-9", - "-10" + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/formatting-1-500-100-1.0-grid-reference.json b/tests/visual_tests/grids/formatting-1-500-100-1.0-grid-reference.json index 087556523..2725f745d 100644 --- a/tests/visual_tests/grids/formatting-1-500-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/formatting-1-500-100-1.0-grid-reference.json @@ -1,16 +1,16 @@ { "keys": [ "", - "-1", - "-2", - "-3", - "-4", - "-5", - "-6", - "-7", - "-8", - "-9", - "-10" + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/formatting-2-500-100-1.0-grid-reference.json b/tests/visual_tests/grids/formatting-2-500-100-1.0-grid-reference.json index 087556523..2725f745d 100644 --- a/tests/visual_tests/grids/formatting-2-500-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/formatting-2-500-100-1.0-grid-reference.json @@ -1,16 +1,16 @@ { "keys": [ "", - "-1", - "-2", - "-3", - "-4", - "-5", - "-6", - "-7", - "-8", - "-9", - "-10" + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/formatting-3-500-100-1.0-grid-reference.json b/tests/visual_tests/grids/formatting-3-500-100-1.0-grid-reference.json index 748a69aa6..ef9f0089b 100644 --- a/tests/visual_tests/grids/formatting-3-500-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/formatting-3-500-100-1.0-grid-reference.json @@ -1,16 +1,16 @@ { "keys": [ "", - "-1", - "-2", - "-3", - "-4", - "-5", - "-6", - "-7", - "-8", - "-9", - "-10" + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/formatting-4-500-100-1.0-grid-reference.json b/tests/visual_tests/grids/formatting-4-500-100-1.0-grid-reference.json index db8079a13..8bddd0118 100644 --- a/tests/visual_tests/grids/formatting-4-500-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/formatting-4-500-100-1.0-grid-reference.json @@ -1,16 +1,16 @@ { "keys": [ "", - "-1", - "-2", - "-3", - "-4", - "-5", - "-6", - "-7", - "-8", - "-9", - "-10" + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/jalign-auto-200-200-1.0-grid-reference.json b/tests/visual_tests/grids/jalign-auto-200-200-1.0-grid-reference.json index 77a9a0ad3..0a2e7e41a 100644 --- a/tests/visual_tests/grids/jalign-auto-200-200-1.0-grid-reference.json +++ b/tests/visual_tests/grids/jalign-auto-200-200-1.0-grid-reference.json @@ -1,7 +1,7 @@ { "keys": [ "", - "-5" + "5" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/line-offset-900-250-1.0-grid-reference.json b/tests/visual_tests/grids/line-offset-900-250-1.0-grid-reference.json index c0742a097..db3484bdf 100644 --- a/tests/visual_tests/grids/line-offset-900-250-1.0-grid-reference.json +++ b/tests/visual_tests/grids/line-offset-900-250-1.0-grid-reference.json @@ -1,14 +1,14 @@ { "keys": [ "", - "-350", - "-400", - "-461", - "-302", - "-112", - "-115", - "-117", - "-118" + "3", + "2", + "1", + "4", + "8", + "7", + "6", + "5" ], "data": {}, "grid": [ @@ -40,14 +40,14 @@ " % %%%%%%%%%% %%%%%%%% %% !!!! ! !!! #### # ### $ $$$$$$$$$ $$$$$$$ $$ $ ", " % %%%%%%%%%%%%%%%%%% %% % !!! ! ! #### # # $ $$$$$$$$$$ $ $$$$$$$$$ $ ", " % %%% %%% %%% %%% % !!!!! ! ! ##### # # $$$$$ $$$$$$$ $ ", - " % %%%% %%%%% % !!!!! !!!!! ### # ### $ $$$$ $ ", + " % %%%% %%%%% !!!!! !!!!! ### # ### $ $$$$ $ ", " %%%%% %%%%% % !! !!!!! #### ##### $ $$$$ $ ", " % %%%% %%% % ! ! !!! # ### $ $$$ $ ", " %%%% % % % !!!! # ## $$$ $$$ $ ", " %%%%% %%% ! !! # ## $$$ $ $$ ", " %%%% &&&& & && %%% ''''' '''' '''''''' !!!!! # (((((( (((( ((( ### $$$$ )))))) )) ) )) $$$$$ ", " % % &&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ! !! '''''''' '''' ! ! # (((((((((( (((( # $$ $ )))))))))) ))))))))))))))) ", - " % &&&&&&&& &&& %%%% !!!! ' ' ! !!! #### ( ( # ### $$$ ))))))))) ) $$ $ ", + " % &&&&&&&& &&& %%%% !!!! ' ! !!! #### ( ( # ### $$$ ))))))))) ) $$ $ ", " % %%%%% !!! !!! #### # ### $$$ $$$ $ ", " %%% % !!! ! ! !!! ## # ### $$$$ $$$ ", " % % % !!! !!!! ### # ### $$$$ $$$$$ ", @@ -56,7 +56,7 @@ " %%%%%%%% % %%%%% % !!!! ! !!!!!! ### # ###### $ $$$$$ $ $$$$$$$$$ ", " % %%%%%%%%%%%%% %%%%%%%%% % !!!!! ! ! !!! ### # # ## ### $ $$$$$$$$$$$$$$$$ $$$$$ $ ", " % %%%%%% %%%%%%%%%%%% % ! !! ! ! ! ## ## # ## ## $$ $$$ $$$$$$$$$$ $ $ ", - " %% %% %%%%%%% %%%% !!!!! !! !!! ! #### ## ## # $$$$$$$$$$$$$$ $$ ", + " %% %% %%%%%%% %%%% !!!!! !!! !!! ! #### ## ## # $ $$$$$$$$$$$$ $$ ", " %%% %%% !!!!!! !!!!!!! !! # ##### ######### ### $$$$$ $$$$$ ", " !!!!! !!! !!!!!!!! ###### ### ######## ", " !! !!!!!!!!!! !!!!!! ## ########## ###### ", diff --git a/tests/visual_tests/grids/lines-1-200-200-1.0-grid-reference.json b/tests/visual_tests/grids/lines-1-200-200-1.0-grid-reference.json index cbab0d249..05e606e8b 100644 --- a/tests/visual_tests/grids/lines-1-200-200-1.0-grid-reference.json +++ b/tests/visual_tests/grids/lines-1-200-200-1.0-grid-reference.json @@ -1,15 +1,15 @@ { "keys": [ "", - "216", - "212", - "210", - "208", - "240", - "200", - "206", - "202", - "204" + "8", + "7", + "6", + "5", + "9", + "1", + "4", + "2", + "3" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/lines-1-400-400-1.0-grid-reference.json b/tests/visual_tests/grids/lines-1-400-400-1.0-grid-reference.json index 01bba2c59..3859dd6c7 100644 --- a/tests/visual_tests/grids/lines-1-400-400-1.0-grid-reference.json +++ b/tests/visual_tests/grids/lines-1-400-400-1.0-grid-reference.json @@ -1,15 +1,15 @@ { "keys": [ "", - "216", - "212", - "210", - "208", - "240", - "202", - "200", - "206", - "204" + "8", + "7", + "6", + "5", + "9", + "2", + "1", + "4", + "3" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/lines-1-600-600-1.0-grid-reference.json b/tests/visual_tests/grids/lines-1-600-600-1.0-grid-reference.json index 5d9834d5a..ae13670ff 100644 --- a/tests/visual_tests/grids/lines-1-600-600-1.0-grid-reference.json +++ b/tests/visual_tests/grids/lines-1-600-600-1.0-grid-reference.json @@ -1,15 +1,15 @@ { "keys": [ "", - "216", - "212", - "210", - "208", - "240", - "202", - "200", - "206", - "204" + "8", + "7", + "6", + "5", + "9", + "2", + "1", + "4", + "3" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/lines-1-800-800-1.0-grid-reference.json b/tests/visual_tests/grids/lines-1-800-800-1.0-grid-reference.json index 8db7d3c07..75c1e0755 100644 --- a/tests/visual_tests/grids/lines-1-800-800-1.0-grid-reference.json +++ b/tests/visual_tests/grids/lines-1-800-800-1.0-grid-reference.json @@ -1,15 +1,15 @@ { "keys": [ "", - "216", - "212", - "210", - "208", - "240", - "202", - "200", - "206", - "204" + "8", + "7", + "6", + "5", + "9", + "2", + "1", + "4", + "3" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/lines-2-200-200-1.0-grid-reference.json b/tests/visual_tests/grids/lines-2-200-200-1.0-grid-reference.json index 79636ad9b..4b34f29ff 100644 --- a/tests/visual_tests/grids/lines-2-200-200-1.0-grid-reference.json +++ b/tests/visual_tests/grids/lines-2-200-200-1.0-grid-reference.json @@ -1,15 +1,15 @@ { "keys": [ "", - "216", - "212", - "210", - "208", - "240", - "200", - "206", - "202", - "204" + "8", + "7", + "6", + "5", + "9", + "1", + "4", + "2", + "3" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/lines-2-400-400-1.0-grid-reference.json b/tests/visual_tests/grids/lines-2-400-400-1.0-grid-reference.json index 782c89483..3aa89339a 100644 --- a/tests/visual_tests/grids/lines-2-400-400-1.0-grid-reference.json +++ b/tests/visual_tests/grids/lines-2-400-400-1.0-grid-reference.json @@ -1,15 +1,15 @@ { "keys": [ "", - "216", - "212", - "210", - "208", - "240", - "202", - "200", - "206", - "204" + "8", + "7", + "6", + "5", + "9", + "2", + "1", + "4", + "3" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/lines-2-600-600-1.0-grid-reference.json b/tests/visual_tests/grids/lines-2-600-600-1.0-grid-reference.json index 7eac49371..6269e3550 100644 --- a/tests/visual_tests/grids/lines-2-600-600-1.0-grid-reference.json +++ b/tests/visual_tests/grids/lines-2-600-600-1.0-grid-reference.json @@ -1,15 +1,15 @@ { "keys": [ "", - "216", - "212", - "210", - "208", - "240", - "202", - "200", - "206", - "204" + "8", + "7", + "6", + "5", + "9", + "2", + "1", + "4", + "3" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/lines-2-800-800-1.0-grid-reference.json b/tests/visual_tests/grids/lines-2-800-800-1.0-grid-reference.json index 5cfb73770..98a1c1a8c 100644 --- a/tests/visual_tests/grids/lines-2-800-800-1.0-grid-reference.json +++ b/tests/visual_tests/grids/lines-2-800-800-1.0-grid-reference.json @@ -1,15 +1,15 @@ { "keys": [ "", - "216", - "212", - "210", - "208", - "240", - "202", - "200", - "206", - "204" + "8", + "7", + "6", + "5", + "9", + "2", + "1", + "4", + "3" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/lines-3-200-200-1.0-grid-reference.json b/tests/visual_tests/grids/lines-3-200-200-1.0-grid-reference.json index cbab0d249..05e606e8b 100644 --- a/tests/visual_tests/grids/lines-3-200-200-1.0-grid-reference.json +++ b/tests/visual_tests/grids/lines-3-200-200-1.0-grid-reference.json @@ -1,15 +1,15 @@ { "keys": [ "", - "216", - "212", - "210", - "208", - "240", - "200", - "206", - "202", - "204" + "8", + "7", + "6", + "5", + "9", + "1", + "4", + "2", + "3" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/lines-3-400-400-1.0-grid-reference.json b/tests/visual_tests/grids/lines-3-400-400-1.0-grid-reference.json index 01bba2c59..3859dd6c7 100644 --- a/tests/visual_tests/grids/lines-3-400-400-1.0-grid-reference.json +++ b/tests/visual_tests/grids/lines-3-400-400-1.0-grid-reference.json @@ -1,15 +1,15 @@ { "keys": [ "", - "216", - "212", - "210", - "208", - "240", - "202", - "200", - "206", - "204" + "8", + "7", + "6", + "5", + "9", + "2", + "1", + "4", + "3" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/lines-3-600-600-1.0-grid-reference.json b/tests/visual_tests/grids/lines-3-600-600-1.0-grid-reference.json index 11d3e3c47..67b4633c9 100644 --- a/tests/visual_tests/grids/lines-3-600-600-1.0-grid-reference.json +++ b/tests/visual_tests/grids/lines-3-600-600-1.0-grid-reference.json @@ -1,15 +1,15 @@ { "keys": [ "", - "216", - "212", - "210", - "208", - "240", - "202", - "200", - "206", - "204" + "8", + "7", + "6", + "5", + "9", + "2", + "1", + "4", + "3" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/lines-3-800-800-1.0-grid-reference.json b/tests/visual_tests/grids/lines-3-800-800-1.0-grid-reference.json index 20ded9891..e7bd89e95 100644 --- a/tests/visual_tests/grids/lines-3-800-800-1.0-grid-reference.json +++ b/tests/visual_tests/grids/lines-3-800-800-1.0-grid-reference.json @@ -1,15 +1,15 @@ { "keys": [ "", - "216", - "212", - "210", - "208", - "240", - "202", - "200", - "206", - "204" + "8", + "7", + "6", + "5", + "9", + "2", + "1", + "4", + "3" ], "data": {}, "grid": [ @@ -80,7 +80,7 @@ " #################################################################################################################################################################################### ", " #################################################################################################################################################################################### ", " #################################################################################################################################################################################### ", - " # # # # # # # # ## # # # # # # # # # # # ## # # ", + " # # # # # # # # # ## # # # # # # # # # # # ## # # ", " ", " ", " ", diff --git a/tests/visual_tests/grids/lines-shield-200-200-1.0-grid-reference.json b/tests/visual_tests/grids/lines-shield-200-200-1.0-grid-reference.json index aa56c929e..9fd0cc8c0 100644 --- a/tests/visual_tests/grids/lines-shield-200-200-1.0-grid-reference.json +++ b/tests/visual_tests/grids/lines-shield-200-200-1.0-grid-reference.json @@ -1,13 +1,13 @@ { "keys": [ "", - "212", - "210", - "208", - "206", - "200", - "202", - "204" + "7", + "6", + "5", + "4", + "1", + "2", + "3" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/lines-shield-400-400-1.0-grid-reference.json b/tests/visual_tests/grids/lines-shield-400-400-1.0-grid-reference.json index cf2b9cb83..cfac4935e 100644 --- a/tests/visual_tests/grids/lines-shield-400-400-1.0-grid-reference.json +++ b/tests/visual_tests/grids/lines-shield-400-400-1.0-grid-reference.json @@ -1,15 +1,15 @@ { "keys": [ "", - "216", - "212", - "210", - "208", - "240", - "206", - "202", - "200", - "204" + "8", + "7", + "6", + "5", + "9", + "4", + "2", + "1", + "3" ], "data": {}, "grid": [ @@ -54,10 +54,10 @@ " ", " ", " ", - " $ $ $ $ $ ", - " $$ $$ $$ $ $ $$ $$ $$ $ $ $ ", - " $$ $$ $$ $ $ $ $$ $$ $ $ $ ", - " $$$ $ $ $ $ $$$ $$$ $ $ $ $$$ $$$ ", + " $ $ $ $ $ $ ", + " $ $$ $ $$ $ $$ $ $$ $ $$ $ $$ ", + " $ $$ $ $$ $ $$ $ $$ $ $$ $ $$ ", + " $$$ $$$ $$$ $$$ $$$ $$$ ", " ", " ", " ", diff --git a/tests/visual_tests/grids/lines-shield-600-600-1.0-grid-reference.json b/tests/visual_tests/grids/lines-shield-600-600-1.0-grid-reference.json index 032dfbf53..a43b5759d 100644 --- a/tests/visual_tests/grids/lines-shield-600-600-1.0-grid-reference.json +++ b/tests/visual_tests/grids/lines-shield-600-600-1.0-grid-reference.json @@ -1,15 +1,15 @@ { "keys": [ "", - "216", - "212", - "210", - "208", - "240", - "202", - "200", - "206", - "204" + "8", + "7", + "6", + "5", + "9", + "2", + "1", + "4", + "3" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/lines-shield-800-800-1.0-grid-reference.json b/tests/visual_tests/grids/lines-shield-800-800-1.0-grid-reference.json index 86da33f64..6ec6c7d12 100644 --- a/tests/visual_tests/grids/lines-shield-800-800-1.0-grid-reference.json +++ b/tests/visual_tests/grids/lines-shield-800-800-1.0-grid-reference.json @@ -1,15 +1,15 @@ { "keys": [ "", - "216", - "212", - "210", - "208", - "240", - "202", - "200", - "206", - "204" + "8", + "7", + "6", + "5", + "9", + "2", + "1", + "4", + "3" ], "data": {}, "grid": [ @@ -97,10 +97,10 @@ " ", " ", " ", - " $$ $$$ $$$ $ $ $ $$ $$ $$$ $$$ $ $ $ $$ $$ $$$ $$$ $ $ $ $$ $$ $$$ $$$ $ $$ ", + " $$ $ $$ $ $$ $ $$ $ $$ $ $$ $ $$ $ $$ $ $$ $ $$ $ $$ $ $$ $ ", " $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ", - " $$ $$ $ $$ $$ $$ $$ $$ $ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ ", - " $ $ $ $ $ $ $ $ $ $ $ $ ", + " $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ $$ ", + " $ $ $ $ $ $ $ $ $ $ $ $ ", " ", " ", " ", diff --git a/tests/visual_tests/grids/list-100-100-1.0-grid-reference.json b/tests/visual_tests/grids/list-100-100-1.0-grid-reference.json index e013c4972..b633480e3 100644 --- a/tests/visual_tests/grids/list-100-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/list-100-100-1.0-grid-reference.json @@ -1,16 +1,16 @@ { "keys": [ "", - "-1", - "-3", - "-5", - "-7", - "-9", - "-2", - "-4", - "-6", - "-8", - "-10" + "1", + "3", + "5", + "7", + "9", + "2", + "4", + "6", + "8", + "10" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/list-150-100-1.0-grid-reference.json b/tests/visual_tests/grids/list-150-100-1.0-grid-reference.json index 9f783721e..aacf5a57b 100644 --- a/tests/visual_tests/grids/list-150-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/list-150-100-1.0-grid-reference.json @@ -1,16 +1,16 @@ { "keys": [ "", - "-4", - "-6", - "-7", - "-8", - "-9", - "-2", - "-1", - "-3", - "-5", - "-10" + "4", + "6", + "7", + "8", + "9", + "2", + "1", + "3", + "5", + "10" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/list-250-100-1.0-grid-reference.json b/tests/visual_tests/grids/list-250-100-1.0-grid-reference.json index 3da38a934..3ead5a120 100644 --- a/tests/visual_tests/grids/list-250-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/list-250-100-1.0-grid-reference.json @@ -1,16 +1,16 @@ { "keys": [ "", - "-1", - "-3", - "-5", - "-7", - "-9", - "-2", - "-4", - "-6", - "-8", - "-10" + "1", + "3", + "5", + "7", + "9", + "2", + "4", + "6", + "8", + "10" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/list-300-100-1.0-grid-reference.json b/tests/visual_tests/grids/list-300-100-1.0-grid-reference.json index 3a5b45829..91ac6b3da 100644 --- a/tests/visual_tests/grids/list-300-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/list-300-100-1.0-grid-reference.json @@ -1,16 +1,16 @@ { "keys": [ "", - "-1", - "-3", - "-5", - "-7", - "-9", - "-2", - "-4", - "-6", - "-8", - "-10" + "1", + "3", + "5", + "7", + "9", + "2", + "4", + "6", + "8", + "10" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/list-400-100-1.0-grid-reference.json b/tests/visual_tests/grids/list-400-100-1.0-grid-reference.json index e802ae4f0..ea336f358 100644 --- a/tests/visual_tests/grids/list-400-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/list-400-100-1.0-grid-reference.json @@ -1,16 +1,16 @@ { "keys": [ "", - "-1", - "-3", - "-5", - "-7", - "-9", - "-10", - "-2", - "-4", - "-6", - "-8" + "1", + "3", + "5", + "7", + "9", + "10", + "2", + "4", + "6", + "8" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/list-600-100-1.0-grid-reference.json b/tests/visual_tests/grids/list-600-100-1.0-grid-reference.json index 5d40ccd6c..a846513de 100644 --- a/tests/visual_tests/grids/list-600-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/list-600-100-1.0-grid-reference.json @@ -1,16 +1,16 @@ { "keys": [ "", - "-2", - "-4", - "-6", - "-8", - "-10", - "-1", - "-3", - "-5", - "-7", - "-9" + "2", + "4", + "6", + "8", + "10", + "1", + "3", + "5", + "7", + "9" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/list-800-100-1.0-grid-reference.json b/tests/visual_tests/grids/list-800-100-1.0-grid-reference.json index b9b246738..692e6adac 100644 --- a/tests/visual_tests/grids/list-800-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/list-800-100-1.0-grid-reference.json @@ -1,16 +1,16 @@ { "keys": [ "", - "-1", - "-2", - "-3", - "-4", - "-5", - "-6", - "-7", - "-8", - "-9", - "-10" + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/rtl-point-200-200-1.0-grid-reference.json b/tests/visual_tests/grids/rtl-point-200-200-1.0-grid-reference.json index c2292c1d4..aa24b0418 100644 --- a/tests/visual_tests/grids/rtl-point-200-200-1.0-grid-reference.json +++ b/tests/visual_tests/grids/rtl-point-200-200-1.0-grid-reference.json @@ -1,7 +1,7 @@ { "keys": [ "", - "-5" + "5" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/shieldsymbolizer-1-490-100-1.0-grid-reference.json b/tests/visual_tests/grids/shieldsymbolizer-1-490-100-1.0-grid-reference.json index d8dbd6f75..a5d255d4b 100644 --- a/tests/visual_tests/grids/shieldsymbolizer-1-490-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/shieldsymbolizer-1-490-100-1.0-grid-reference.json @@ -1,16 +1,16 @@ { "keys": [ "", - "-8", - "-5", - "-1", - "-2", - "-6", - "-7", - "-9", - "-10", - "-3", - "-4" + "8", + "5", + "1", + "2", + "6", + "7", + "9", + "10", + "3", + "4" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/shieldsymbolizer-1-495-100-1.0-grid-reference.json b/tests/visual_tests/grids/shieldsymbolizer-1-495-100-1.0-grid-reference.json index f6efaf5b4..b0f75845b 100644 --- a/tests/visual_tests/grids/shieldsymbolizer-1-495-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/shieldsymbolizer-1-495-100-1.0-grid-reference.json @@ -1,16 +1,16 @@ { "keys": [ "", - "-8", - "-5", - "-1", - "-2", - "-3", - "-6", - "-7", - "-9", - "-10", - "-4" + "8", + "5", + "1", + "2", + "3", + "6", + "7", + "9", + "10", + "4" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/shieldsymbolizer-1-497-100-1.0-grid-reference.json b/tests/visual_tests/grids/shieldsymbolizer-1-497-100-1.0-grid-reference.json index be04f3405..b190871af 100644 --- a/tests/visual_tests/grids/shieldsymbolizer-1-497-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/shieldsymbolizer-1-497-100-1.0-grid-reference.json @@ -1,16 +1,16 @@ { "keys": [ "", - "-8", - "-5", - "-1", - "-2", - "-3", - "-6", - "-7", - "-9", - "-10", - "-4" + "8", + "5", + "1", + "2", + "3", + "6", + "7", + "9", + "10", + "4" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/shieldsymbolizer-1-498-100-1.0-grid-reference.json b/tests/visual_tests/grids/shieldsymbolizer-1-498-100-1.0-grid-reference.json index 52bf4d53a..ca0db3884 100644 --- a/tests/visual_tests/grids/shieldsymbolizer-1-498-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/shieldsymbolizer-1-498-100-1.0-grid-reference.json @@ -1,16 +1,16 @@ { "keys": [ "", - "-8", - "-5", - "-1", - "-2", - "-3", - "-6", - "-7", - "-9", - "-10", - "-4" + "8", + "5", + "1", + "2", + "3", + "6", + "7", + "9", + "10", + "4" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/shieldsymbolizer-1-499-100-1.0-grid-reference.json b/tests/visual_tests/grids/shieldsymbolizer-1-499-100-1.0-grid-reference.json index e7e48c88a..1b92a2855 100644 --- a/tests/visual_tests/grids/shieldsymbolizer-1-499-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/shieldsymbolizer-1-499-100-1.0-grid-reference.json @@ -1,16 +1,16 @@ { "keys": [ "", - "-8", - "-5", - "-1", - "-2", - "-3", - "-6", - "-7", - "-9", - "-10", - "-4" + "8", + "5", + "1", + "2", + "3", + "6", + "7", + "9", + "10", + "4" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/shieldsymbolizer-1-500-100-1.0-grid-reference.json b/tests/visual_tests/grids/shieldsymbolizer-1-500-100-1.0-grid-reference.json index 2a6f9bbaa..26459bd0f 100644 --- a/tests/visual_tests/grids/shieldsymbolizer-1-500-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/shieldsymbolizer-1-500-100-1.0-grid-reference.json @@ -1,16 +1,16 @@ { "keys": [ "", - "-8", - "-5", - "-1", - "-2", - "-3", - "-6", - "-7", - "-9", - "-10", - "-4" + "8", + "5", + "1", + "2", + "3", + "6", + "7", + "9", + "10", + "4" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/shieldsymbolizer-1-501-100-1.0-grid-reference.json b/tests/visual_tests/grids/shieldsymbolizer-1-501-100-1.0-grid-reference.json index d72cfe602..88cc49451 100644 --- a/tests/visual_tests/grids/shieldsymbolizer-1-501-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/shieldsymbolizer-1-501-100-1.0-grid-reference.json @@ -1,16 +1,16 @@ { "keys": [ "", - "-8", - "-5", - "-1", - "-2", - "-3", - "-6", - "-7", - "-9", - "-10", - "-4" + "8", + "5", + "1", + "2", + "3", + "6", + "7", + "9", + "10", + "4" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/shieldsymbolizer-1-502-100-1.0-grid-reference.json b/tests/visual_tests/grids/shieldsymbolizer-1-502-100-1.0-grid-reference.json index b48db9f64..050335c81 100644 --- a/tests/visual_tests/grids/shieldsymbolizer-1-502-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/shieldsymbolizer-1-502-100-1.0-grid-reference.json @@ -1,16 +1,16 @@ { "keys": [ "", - "-8", - "-5", - "-1", - "-2", - "-6", - "-7", - "-9", - "-10", - "-3", - "-4" + "8", + "5", + "1", + "2", + "6", + "7", + "9", + "10", + "3", + "4" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/shieldsymbolizer-1-505-100-1.0-grid-reference.json b/tests/visual_tests/grids/shieldsymbolizer-1-505-100-1.0-grid-reference.json index bc8af027f..aa2e746c4 100644 --- a/tests/visual_tests/grids/shieldsymbolizer-1-505-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/shieldsymbolizer-1-505-100-1.0-grid-reference.json @@ -1,16 +1,16 @@ { "keys": [ "", - "-8", - "-5", - "-1", - "-2", - "-6", - "-7", - "-9", - "-10", - "-3", - "-4" + "8", + "5", + "1", + "2", + "6", + "7", + "9", + "10", + "3", + "4" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/shieldsymbolizer-1-510-100-1.0-grid-reference.json b/tests/visual_tests/grids/shieldsymbolizer-1-510-100-1.0-grid-reference.json index 04693f989..f0706c77d 100644 --- a/tests/visual_tests/grids/shieldsymbolizer-1-510-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/shieldsymbolizer-1-510-100-1.0-grid-reference.json @@ -1,16 +1,16 @@ { "keys": [ "", - "-8", - "-5", - "-1", - "-2", - "-6", - "-7", - "-9", - "-10", - "-3", - "-4" + "8", + "5", + "1", + "2", + "6", + "7", + "9", + "10", + "3", + "4" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/simple-100-100-1.0-grid-reference.json b/tests/visual_tests/grids/simple-100-100-1.0-grid-reference.json index a93d7f62e..d250023b9 100644 --- a/tests/visual_tests/grids/simple-100-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/simple-100-100-1.0-grid-reference.json @@ -1,14 +1,14 @@ { "keys": [ "", - "-2", - "-5", - "-8", - "-1", - "-9", - "-10", - "-3", - "-7" + "2", + "5", + "8", + "1", + "9", + "10", + "3", + "7" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/simple-150-100-1.0-grid-reference.json b/tests/visual_tests/grids/simple-150-100-1.0-grid-reference.json index 5e3afa8a4..8b25d98e0 100644 --- a/tests/visual_tests/grids/simple-150-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/simple-150-100-1.0-grid-reference.json @@ -1,14 +1,14 @@ { "keys": [ "", - "-2", - "-7", - "-4", - "-10", - "-1", - "-6", - "-3", - "-8" + "2", + "7", + "4", + "10", + "1", + "6", + "3", + "8" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/simple-250-100-1.0-grid-reference.json b/tests/visual_tests/grids/simple-250-100-1.0-grid-reference.json index 8f11c4ca2..f52fcbdfa 100644 --- a/tests/visual_tests/grids/simple-250-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/simple-250-100-1.0-grid-reference.json @@ -1,16 +1,16 @@ { "keys": [ "", - "-2", - "-5", - "-8", - "-10", - "-1", - "-4", - "-7", - "-3", - "-6", - "-9" + "2", + "5", + "8", + "10", + "1", + "4", + "7", + "3", + "6", + "9" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/simple-300-100-1.0-grid-reference.json b/tests/visual_tests/grids/simple-300-100-1.0-grid-reference.json index d2bcad422..3bf047bed 100644 --- a/tests/visual_tests/grids/simple-300-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/simple-300-100-1.0-grid-reference.json @@ -1,16 +1,16 @@ { "keys": [ "", - "-2", - "-5", - "-8", - "-10", - "-1", - "-4", - "-7", - "-3", - "-6", - "-9" + "2", + "5", + "8", + "10", + "1", + "4", + "7", + "3", + "6", + "9" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/simple-400-100-1.0-grid-reference.json b/tests/visual_tests/grids/simple-400-100-1.0-grid-reference.json index a0c3ee225..b88c666e5 100644 --- a/tests/visual_tests/grids/simple-400-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/simple-400-100-1.0-grid-reference.json @@ -1,16 +1,16 @@ { "keys": [ "", - "-2", - "-4", - "-6", - "-8", - "-10", - "-1", - "-3", - "-5", - "-7", - "-9" + "2", + "4", + "6", + "8", + "10", + "1", + "3", + "5", + "7", + "9" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/simple-600-100-1.0-grid-reference.json b/tests/visual_tests/grids/simple-600-100-1.0-grid-reference.json index 8fbe2e8ab..a6638f296 100644 --- a/tests/visual_tests/grids/simple-600-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/simple-600-100-1.0-grid-reference.json @@ -1,16 +1,16 @@ { "keys": [ "", - "-1", - "-3", - "-5", - "-7", - "-9", - "-2", - "-4", - "-6", - "-8", - "-10" + "1", + "3", + "5", + "7", + "9", + "2", + "4", + "6", + "8", + "10" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/simple-800-100-1.0-grid-reference.json b/tests/visual_tests/grids/simple-800-100-1.0-grid-reference.json index a884777f0..dbd1b4bca 100644 --- a/tests/visual_tests/grids/simple-800-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/simple-800-100-1.0-grid-reference.json @@ -1,16 +1,16 @@ { "keys": [ "", - "-1", - "-2", - "-3", - "-4", - "-5", - "-6", - "-7", - "-8", - "-9", - "-10" + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/simple-E-500-100-1.0-grid-reference.json b/tests/visual_tests/grids/simple-E-500-100-1.0-grid-reference.json index c83d9f9bc..8a1b162de 100644 --- a/tests/visual_tests/grids/simple-E-500-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/simple-E-500-100-1.0-grid-reference.json @@ -1,7 +1,7 @@ { "keys": [ "", - "-5" + "5" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/simple-N-500-100-1.0-grid-reference.json b/tests/visual_tests/grids/simple-N-500-100-1.0-grid-reference.json index ecd2f97d0..b049b10d1 100644 --- a/tests/visual_tests/grids/simple-N-500-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/simple-N-500-100-1.0-grid-reference.json @@ -1,7 +1,7 @@ { "keys": [ "", - "-5" + "5" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/simple-NE-500-100-1.0-grid-reference.json b/tests/visual_tests/grids/simple-NE-500-100-1.0-grid-reference.json index cc5e49b81..aa030d211 100644 --- a/tests/visual_tests/grids/simple-NE-500-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/simple-NE-500-100-1.0-grid-reference.json @@ -1,7 +1,7 @@ { "keys": [ "", - "-5" + "5" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/simple-NW-500-100-1.0-grid-reference.json b/tests/visual_tests/grids/simple-NW-500-100-1.0-grid-reference.json index 3ac668c0d..1d054206d 100644 --- a/tests/visual_tests/grids/simple-NW-500-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/simple-NW-500-100-1.0-grid-reference.json @@ -1,7 +1,7 @@ { "keys": [ "", - "-5" + "5" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/simple-S-500-100-1.0-grid-reference.json b/tests/visual_tests/grids/simple-S-500-100-1.0-grid-reference.json index 60b30e0f4..28fb8011c 100644 --- a/tests/visual_tests/grids/simple-S-500-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/simple-S-500-100-1.0-grid-reference.json @@ -1,7 +1,7 @@ { "keys": [ "", - "-5" + "5" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/simple-SE-500-100-1.0-grid-reference.json b/tests/visual_tests/grids/simple-SE-500-100-1.0-grid-reference.json index 125524dd2..691c6d843 100644 --- a/tests/visual_tests/grids/simple-SE-500-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/simple-SE-500-100-1.0-grid-reference.json @@ -1,7 +1,7 @@ { "keys": [ "", - "-5" + "5" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/simple-SW-500-100-1.0-grid-reference.json b/tests/visual_tests/grids/simple-SW-500-100-1.0-grid-reference.json index 794db25f2..6f39d2c38 100644 --- a/tests/visual_tests/grids/simple-SW-500-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/simple-SW-500-100-1.0-grid-reference.json @@ -1,7 +1,7 @@ { "keys": [ "", - "-5" + "5" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/grids/simple-W-500-100-1.0-grid-reference.json b/tests/visual_tests/grids/simple-W-500-100-1.0-grid-reference.json index 9725865bc..e52b12abb 100644 --- a/tests/visual_tests/grids/simple-W-500-100-1.0-grid-reference.json +++ b/tests/visual_tests/grids/simple-W-500-100-1.0-grid-reference.json @@ -1,7 +1,7 @@ { "keys": [ "", - "-5" + "5" ], "data": {}, "grid": [ diff --git a/tests/visual_tests/styles/simple.xml b/tests/visual_tests/styles/simple.xml index e965dc2a5..a10fbdac5 100644 --- a/tests/visual_tests/styles/simple.xml +++ b/tests/visual_tests/styles/simple.xml @@ -12,8 +12,8 @@ diff --git a/tests/visual_tests/test.py b/tests/visual_tests/test.py index f1ae4866f..96de8d698 100755 --- a/tests/visual_tests/test.py +++ b/tests/visual_tests/test.py @@ -23,7 +23,7 @@ defaults = { 'cairo': False, # disabled for 2.2.x since cairo tests are unstable (springmeyer) #'cairo': mapnik.has_cairo(), - 'grid': False + 'grid': True } sizes_many_in_big_range = [(800, 100), (600, 100), (400, 100), From 018bdc4f6dd3cdb13e041b94e3734fb10f554028 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 20 May 2013 22:41:59 -0700 Subject: [PATCH 050/110] fix interplay of point symbolizer/cairo/scale_factor --- src/cairo_renderer.cpp | 19 +++++++++--------- .../simple-400-100-2.0-cairo-reference.png | Bin 3391 -> 3372 bytes .../simple-600-100-1.0-cairo-reference.png | Bin 2103 -> 2088 bytes .../simple-800-100-2.0-cairo-reference.png | Bin 4920 -> 4935 bytes tests/visual_tests/test.py | 2 +- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/cairo_renderer.cpp b/src/cairo_renderer.cpp index 16d331421..6dcd628b8 100644 --- a/src/cairo_renderer.cpp +++ b/src/cairo_renderer.cpp @@ -669,6 +669,15 @@ void cairo_renderer_base::process(point_symbolizer const& sym, if (marker) { + box2d const& bbox = (*marker)->bounding_box(); + coord2d center = bbox.center(); + + agg::trans_affine tr; + evaluate_transform(tr, feature, sym.get_image_transform()); + agg::trans_affine_translation recenter(-center.x, -center.y); + agg::trans_affine recenter_tr = recenter * tr; + box2d label_ext = bbox * recenter_tr * agg::trans_affine_scaling(scale_factor_); + for (unsigned i = 0; i < feature.num_geometries(); ++i) { geometry_type const& geom = feature.get_geometry(i); @@ -689,15 +698,7 @@ void cairo_renderer_base::process(point_symbolizer const& sym, prj_trans.backward(x, y, z); t_.forward(&x, &y); - - double dx = 0.5 * (*marker)->width(); - double dy = 0.5 * (*marker)->height(); - agg::trans_affine tr; - evaluate_transform(tr, feature, sym.get_image_transform()); - box2d label_ext (-dx, -dy, dx, dy); - label_ext *= tr; - label_ext *= agg::trans_affine_translation(x,y); - label_ext *= agg::trans_affine_scaling(scale_factor_); + label_ext.re_center(x,y); if (sym.get_allow_overlap() || detector_->has_placement(label_ext)) { diff --git a/tests/visual_tests/images/simple-400-100-2.0-cairo-reference.png b/tests/visual_tests/images/simple-400-100-2.0-cairo-reference.png index cd18a67dfaff3031fea6b4e652e1ceedd6d5d649..aab6f1a337f7ef8d68fa9ca4684d1d002860b5d4 100644 GIT binary patch delta 3073 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 B>X-lk delta 3077 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

7}m=9qex&A1gMF=iUGwCsrfY}ScH9L~9d zY%ykqFhII-OJI>~EPEMbqNd?eQf31?ePt*UFj-S*KzU4KY2_X49f$6)c*J8wXB1(T z*H9aAf2ezz)Fv2%rM6XCOmbjVBh9KGX$*{DhL3E_1*TSU#*$vO(P^JPoyWzMjO&Wc3>n|*;O68{ zr6EQNadVNLrV+l17uENL{#^Te$?gkZ4st{=yo0hEYjn|p)gvK>*vqf1kYCuE-L+Ca zjhy_@&|{-HL)>}K-f!nT{RMT@WCj)lOw<}!;?!Kg<;vF9hu_<6R?-`Njkj*k6k7p| zLjhZlGn()deu})*^|aQD*`n!v62ts-KlSI*MJ{Bpl>=$Ct4aJu+H{lc&^4@bW;H9JkdKiyjpI{1j_ zW$7*65bpS~W4vhqX*N1ciNa<)%h?9%TCHroLOTN9BzkLyb9z0DGPh@Nv50Mcf96i? z9BP&Qn35+5Nz|NZ%nmI@Gr4jfu&nuqZ7@y$`ocA7Fax8_P+#=a)Y+{P#WJw#8T6hk zS4F8Jj?s8|FY}zOp#5D}?h;1dLto|6S@{7Z+bE6_RddnBII%a?;+i-5u`%F(acr_O zE3yA7b!D3j{Jb_+gTcjywdNiAd-Llvh(4>*T^J2T#n!V7d_rkpYE+I+`wev4o!`Zi z=qGB*E&TTs9UZoZR?sgkw<-ysQkhI$FiY01Jwg0uTaJ@KSkpt(RSU!Fgr2(tSluJ& zqG>S#GHg%vAuDI<*cO{a6$NCyeb~3y`z>&1aW#zRg_%1H>Qc+cq>%R(WcBowfk$3h-Vtc12+}=uF7H=Ln2y*V%=84-Tg;9xe7hLTF$+ z8qh>pLj48+#4XnJ9)as)FCSt>vx=vNRuqx*M79bJjC)E!2)t1qL);uya9h2c`T?m; zyz2fPs(iW;2a1m;;dJ@c&3L6;P0h>q9U*8!D?mFtFGLi}$nJeFvxBv)9Bp6^9f-N# zdWz6{Cu@XGm>Myn?qR>mqfmkeG>+PEce4ZyjGYP)rV4!3VVDp?3Fv3|k>312e?NS> zlIpUJue5F7Ta<0vkQZNTNZ9Pm!`?_!8%P^q<{CV0ugMT+oKXAZu>Lk&YUQ~7_k{($ zvza}5>lj32H|0#QtBOreU3dKEQnTzws`nRt=4XPYR(-v;a!SudJ|uY@rt1kKrhvV> z?X`*HyGi0~RzgXLAmqgh9g#Ie2Joj)+11o)$;HKKg*KCgyn@!XBK~1ttE`~w9demj z*DEe1y&w=}R@O!7tff(NO|5|v^m#74{(QS(dsX7WsS#D*RZ>MAdUW9vgqXh-dgyyJ zmd>A$0rc0~^p~6<@?d$5`uG+&>#s^dDQ$w8riMb5wYn zXF0Of{5Hf8#@!JI{)8|oNt@>I$n#5*=feaw!s|%$M9${V8E1gZXsm;8+7 z?>rlYY6NIqVX|9aoOWzTHo_4uQT!l$Ov|4W^$V)YjS07Zdr{;mrSrA& zpndjog56h}Mm@=EQn-h=Bh?(%*Fr1tI}6D^hzb31d@^s!_W~{~nU+&3jdY*+ddfn* zab{h&T#pe~RKLeU{Y0K30p!o`qP2f5897ZqaG4$Xk1=m-_DRpnTd?~ju5VNt-^+(X zuAV*WBh%)}uWs{K2UwA;^LHw~75MWEYN$li{gHEbu8G@`%gEM<2*Id=9+YU?i)h1V z3EFKCvDXAtod!ggS?^Z}tJk}8d)pHzS#-GFu@LOx~AW-LY#1&6X4sz`~`Adms0Pzsoj$`sq zQ29`9=pQ6doXzD2DYQ;a_qv*eAyX_GW-uFyCz*H|=}@jd_-f1@{SQ!$qAi#^RPTw$ zd#=`mBG8yvB5=d6@Q11<{L#G#nUe4sQ$L#tKZzZp%r_{44ih53YQqxe8Glg>671o4 zS#*T8ILn`?Wgnt*g>a88?$Bd_jb5L=QNTF;c0NIWWW=BVijc#;e|;)5<&;qF05_?FNwH0&gzPRodYNkrSU;Hs zj~giuTaSEYFJwE#=?~lb4_nb3$7)M(#2>p27&ZQ4r@{X>U#X`0lzoGus4NNDPe1Ce zvA8G+>F5vMmP?pW@-%J#&^SF`&`&w$IQHbVme={*m$mpKONpZ6_3pngUDxv8ibRwn z2;*8)$YK>(Xf6TJZbOyh)<`5!Mpl|N$(fn$=ANT9~GCyBR|uZ)n{$j zK;ur$@3>aKAX7G-9yzzT?Oj_H$8^1FJu2&s@TBQ3U>UtPnoxk6Cz(Ui^JPf7rX7B) zrJ14>iJW%3pIF%OSp;T!Qx|Qj89*D#e)j5d<0oxb5!uFl0YjB-e91@oP zYfW2&w6Xnc^CAI>gN>n9dqBxoKY`9!sTsw1Gt`c>?33w{Frl;LcP;lLw)SXSCFW*N zi|#*5R@M7mF`W!a)g&})UWc`-liWgIjA-hGydYhpm@eo9_2<|X#|ocOW#h1j zh)f}ZQ91jsSl6co-7ATjW>LBJLVB?1l`i&vQ8ToK#~^aoUSW=0r!e8*%0m6`kx_wk z&@SV>(4@Z{EMVSBPXlRHPP$TSJTB1R$V1!}LxXmK|2gol@%K1-8FJ3T_X>Ej=Y)H+ zOxvwc{!{KxD`3!hg_?7aXx ziRzmRCVm4TlW|W<+UT8ahcoqbdRRir47rn!M!P(zwb7J+1CfdME5pcv*%Ny*GBR2+ NeeK&fsDJh}4iszyqH9-|s%$hjaGh-fN9H#@zE;W2`R{H4?ZgdLogSR4WZcZP7oX zqeEzzT-37;%>H!~W;55oqYb?Xhu;gunCi{a^P$nxm4E;GO%<6;R9akUu8e<_(>NTz zdl&K0s$#nev~To^E*>0xb+6d|t=2Q`mbxJ+?(UIC2T?x1d(NSnizxk8h!2+L_d9!j z7mvBygbbbbpCMR%cPaHPQxdHPlzKIZLS9qdCuJ?}&;1F@T;e5}`BRY379WTd+(?gZ zWYWByNF$u>eby%MR-Tj&&GOJU#JcQVBgoF|JS6w1kiLBYy&5OSLw^1AY`5p)b7!Pn z-$Kj2kEdzxA5z_i#4=e1F=FC^2w8 zDPHvktLqo{2mhU|k;89%Yf)J|Gxc0*J#p?S-e|4;Qt&W0G2zzD zK_cyed>*y!qcO|UFS823TqmdtJ84s>_dM9(ogH6Nz8&- z_6X81oTzRJ9yLY;gEJP5CpJ8)y3iiX;2`jPF%{*q77E;H&(ME^>gY|)A^Tf(8AUkY zl6U%aCU!mKTtLqYYO%wJla|~@naQX9)XM^|_*VCl%&INqMwE{@TbgceU*R5a1(QK;Oy0|dqrBioSqxyr;Qa!2fuZ^-Q22UIRy@eWD*`5}?JQRGE^v99Otm#m-9 z_&>9DFoToMVmSb&=j5kU&)cUqw?b4Fc%)N(k|u9lFHaKNVPX@|WX}vo84JZN8jY_7 zg|>UG=@BokS48foI~EL_!ATHoVq=tjZR@W-jcWl5|Jsu|2m)DLw(v`DTft9*112GD zqgf-CmT+vOUN#D$aZ|N^$tMD zNS0p-Jph-4H{*AVc-f5-z+=hm9j;2$ZroGY#FzSQ$&f0>51-=tj423z z746a*mRwHHtG>!2B-q7vq#=@7^U0r#=tc+zoeR%xv`9&i6_%_74?lXiu?A~ZPA*$B zqxnc+6mZTlA0GtY@eBAVyXU&M<+QLJG>oMJ1WN733=+-ZSfq~v92--rMaT|c(L(%@hEA=&X+`$HF1_m@_U5E!HSEh zSqF13=x1);nI4zgD#hPPo^WG+`SUF!_Ul>8^sgf}^RHKsd|ov*HkNZV?8VJeq2GbY zd1BAdvwTAgZol}(4$*5uI8I_A=OZ441XhfbRpu2euhdsEhPt`R^VDa+`<{1F!AXkP ztR-z@dA4HV_fJs9HWGtOH+lIz^YLT4*fbTf)hH+78C>a zjBxLbPEZjRt>lu)Bw0hh&fV8gb43UG)a;*f1#@TLTv6#S@C3Z;d@5OvNt7)7P;h~VXMOF36qgpfPiH(v`HUwFD zW81_@rF)thjz>dZR3N*dZ&WvY1LChabX;d9mY^1d?j=q}j zgbkA0xh~B~wIS-Wp)`C@Sq?MMRw4O57RZ$ANCRpJNr(GTs|~r{_UdGi=0?g$&d~iO zeum*9^N&6`A(zijxfvc09jMmnytSH}1z01+^IJDJ(~5nHrv;%${JP_Ju4)hc#DVGk zWnDDO#X}T=$lYjU3nF7dtnfG%V^L< zzF=y%`QYryc=|EOy8t}Dqe=n2o?pI++Il6=aF}_WW)y_Kb2sV!NSioobb$MBm+CcM zy(_${*2R|;90X0m7$)X*Ou5u8?!}35oqL*;DDU{^b^<`YSNRDi*As)VCMv=9Ih?i zOm{OnI>F-gT0W%*7YAA0KDFzzc<<55iP79vzG3SzV+70NroGdNbCul$b7@?9{D~Gj z#-QlA30F-IYwRy)iiT>!=hSoidp<)xSrmnE_h4|jnu3;;;bW9c1`pu^AXS^_;Xce_ zxR6SJZ9Ibn*j^Bd{_rklcv5Bi=|uor6q79lXQqF%_&jCKcu2FO)4b5(t=2Tl!KGE{ zWG}CXA{d}`t-F@5mSL@X<8YVSuI^&YmDl; zikh4>XyLzA9M$$Ed*pWaSSY?{=xG=R3GXadMFPI;ruis1sWsvQ=Z`!H3nWSV+U}#0 zkxvwS0PJw=30TiHk(;mDaq64G3PpR2hgJcChIa^N6`bFQqfH zk(TbkrQi^K5Em2p1s%)LnMwx@M@b!_Ah}RS6tn z#e?&?<5k;2`ZCXWvAcq8gvv9@24P%YQ47~#|xm@ECI(_vQf z2Gv<@_nPXc%-7_=gMzwb2QMEmbtKQPy#tLbA;eOoJeGJh4}QjjV`658pG-Q{PObOJ z;o@`s+9`UxZy+0?=A7A==v23di=e9oDFx;}ra~$Y`AjH}`!r*p60$(m$ z+A|VTJcmJt=+)_}^AeA5d5@?ZsPXw)RFsIkcv41^2?jcqOxxAU&_xMi;4uKuq_8(_ zDRZG#+h#A+odSSc{Tqfsu;BI7xEp5)R}&`K4u@@Tu)mW~tD4{Lf0E~s!dX~#5OM2z z*Y$Rek!HW6EVqt4uO;%|X{ZdGEZ&6TTh=Jd~qxxrA&a6u*wWgTDd<(AAYZs8o$v8voE403-utp5nc*g91zDvp_RX9-*Dp9!Cn(2Dxz|qmbCa~_qEzwul^=Y1>7g2xB9V; z#z+pAZ`b@MyqLQK=YlCFkS*}Q51z2nSckgJxImLvVskQa2Qlh#s7#ueTx`t!h89ix ztDQm{?}P+`J$Q3EO3Lr5aW`&-9?@)7kj0%XXJe``Mx<`G?rz|jT$|rGhr_OEyeUZY zrHMEoq+=*uE`Ug%2s{kPE~M|y$7^3>k0&aYY%$t9Mc6U+-=Tckv~;>}sjj?uj3cl!uWN!sMU@q}@5D^eB`cmit>>CP9)|I3so-|45)&%MuR~!VA2R0Do(RMErS!-%Z*Ka9|ag)Jj+tC<6Kd16E zjJ>#0P?F#{AR|%O5~|9wAhkRmcXhsm+J&l1?Yo$$^2FwFO(19MCI1z-a`a-JwC+9= zrlU#xFiv$7&$0lWP3Vh$$W9W3Sc+>~v&U z=Sw^=w?oe@&UcJZ5o#($VYt;w8V7w>cEJ6U^O!-0S?-{76X7N@AnA9fH;+5)YjN;X z@1%OdEOS9if$Amb#9{6GP0rtL<=CY>4&A|EH1c=^k7-28%LUL>>yBVz99w0gyY!A> z$njjjPS;(xv59_5?)EC}C{uUC@o&5N9U~7q3T2zv86sw%t;+K?emYJ8ns(3W7@*8YhT<<2re{X1OB_E4Pc z{AxFpe3^SR__Y?#4_d{Xr0t2L_^t#ztC0xv#|#MS+F7eB24_+X8W*hDw+;@DZJJgb z|L4gtpPZnAR4st0Q}%|6jyD$hFj|m_A^D2sOpAGZx-*$5q*AKlu}IjF$PI!ZIvI_E z>BgjGcL_HUjylj|bkOXv{+&K+<-?4S`0}RwvDYHBs?diSoBP0Eb!+-Z(%#T=PKVV} z3bw1C>P6Rdo#TINeE&m#)T5c&Zhv={WIHm`KIj{9B(Sb9v8M>&o1%3~IXE$3y=+*> zk6xX=K^KJV4~Ih#3uF^Wlxq{0T~17o7P*EbpBw9r3uED8nro(6ry*=V~Z*KLAKV)*d(S{?sSDU>@OFQNZUj&vxvC;52 zEkw!S6kZLKeT{FLYrI`UACKZ)jcat^O9E zk2{ZfjoyI#zu#XEtN@q3&33Qr9eQ#J6anSix7K2RFt@53G9N69u11fZ7RI52ZytB`=xySL;GZI9(U25dAYw)9I>02*5LIK!8Hb+PP~kC2~G(5x5ZCjfiKn%zmk7Rxa#Q-(l-?ryI_%> z#}f-?{HB=ykfEgn1ougzO1&rcf`Y&2*V#_ Date: Mon, 20 May 2013 22:45:39 -0700 Subject: [PATCH 051/110] overwrite all trivially failing cairo visual tests from os 10.8 (cairo 1.12.4 / freetype 2.4.11) --- .../collision-600-400-1.0-cairo-reference.png | Bin 27957 -> 28021 bytes .../collision-600-400-2.0-cairo-reference.png | Bin 27238 -> 27260 bytes ...ine-offset-900-250-1.0-cairo-reference.png | Bin 14030 -> 14490 bytes ...ine-offset-900-250-2.0-cairo-reference.png | Bin 14527 -> 14477 bytes .../lines-1-200-200-1.0-cairo-reference.png | Bin 1982 -> 1978 bytes .../lines-1-200-200-2.0-cairo-reference.png | Bin 1156 -> 1149 bytes .../lines-1-400-400-2.0-cairo-reference.png | Bin 4335 -> 4306 bytes .../lines-1-600-600-1.0-cairo-reference.png | Bin 10868 -> 10233 bytes .../lines-1-600-600-2.0-cairo-reference.png | Bin 7761 -> 7691 bytes .../lines-1-800-800-1.0-cairo-reference.png | Bin 14355 -> 14402 bytes .../lines-1-800-800-2.0-cairo-reference.png | Bin 13210 -> 13187 bytes .../lines-2-200-200-1.0-cairo-reference.png | Bin 2305 -> 2301 bytes .../lines-2-200-200-2.0-cairo-reference.png | Bin 1156 -> 1149 bytes .../lines-2-400-400-2.0-cairo-reference.png | Bin 5070 -> 5038 bytes .../lines-2-600-600-1.0-cairo-reference.png | Bin 8759 -> 8776 bytes .../lines-2-600-600-2.0-cairo-reference.png | Bin 6654 -> 6596 bytes .../lines-2-800-800-1.0-cairo-reference.png | Bin 18113 -> 18157 bytes .../lines-2-800-800-2.0-cairo-reference.png | Bin 10826 -> 10793 bytes .../lines-3-200-200-1.0-cairo-reference.png | Bin 1982 -> 1978 bytes .../lines-3-200-200-2.0-cairo-reference.png | Bin 1156 -> 1149 bytes .../lines-3-400-400-2.0-cairo-reference.png | Bin 4335 -> 4306 bytes .../lines-3-600-600-1.0-cairo-reference.png | Bin 10347 -> 10379 bytes .../lines-3-600-600-2.0-cairo-reference.png | Bin 7860 -> 7806 bytes .../lines-3-800-800-1.0-cairo-reference.png | Bin 15436 -> 15426 bytes .../lines-3-800-800-2.0-cairo-reference.png | Bin 13381 -> 13350 bytes ...nes-shield-400-400-1.0-cairo-reference.png | Bin 4979 -> 4387 bytes ...nes-shield-400-400-2.0-cairo-reference.png | Bin 5019 -> 4915 bytes ...nes-shield-800-800-1.0-cairo-reference.png | Bin 9409 -> 8349 bytes ...nes-shield-800-800-2.0-cairo-reference.png | Bin 10457 -> 9346 bytes .../list-150-100-2.0-cairo-reference.png | Bin 1629 -> 1631 bytes .../list-300-100-2.0-cairo-reference.png | Bin 3115 -> 3113 bytes .../list-400-100-1.0-cairo-reference.png | Bin 2156 -> 2153 bytes .../list-600-100-1.0-cairo-reference.png | Bin 2083 -> 2071 bytes .../list-800-100-2.0-cairo-reference.png | Bin 4507 -> 4478 bytes ...lti-policy-600-400-2.0-cairo-reference.png | Bin 7236 -> 7187 bytes ...n-hex-grid-400-600-1.0-cairo-reference.png | Bin 22652 -> 23241 bytes ...n-hex-grid-600-400-1.0-cairo-reference.png | Bin 18139 -> 18468 bytes ...er-on-line-600-400-1.0-cairo-reference.png | Bin 2426 -> 2442 bytes ...er-on-line-600-400-2.0-cairo-reference.png | Bin 2828 -> 2862 bytes ...g-eq-width-600-400-1.0-cairo-reference.png | Bin 13264 -> 13313 bytes ...g-eq-width-600-400-2.0-cairo-reference.png | Bin 15129 -> 15226 bytes ...th-overlap-600-400-1.0-cairo-reference.png | Bin 15310 -> 15287 bytes ...th-overlap-600-400-2.0-cairo-reference.png | Bin 18445 -> 18483 bytes ...ound-image-257-256-1.0-cairo-reference.png | Bin 13246 -> 13373 bytes ...ound-image-400-600-1.0-cairo-reference.png | Bin 15496 -> 15627 bytes ...ound-image-400-600-2.0-cairo-reference.png | Bin 23396 -> 23048 bytes ...ound-image-600-400-1.0-cairo-reference.png | Bin 21317 -> 21884 bytes ...ound-image-600-400-2.0-cairo-reference.png | Bin 32087 -> 31932 bytes ..._on_points-500-100-1.0-cairo-reference.png | Bin 994 -> 986 bytes ..._on_points-500-100-2.0-cairo-reference.png | Bin 1228 -> 1214 bytes ...mbolizer-1-498-100-2.0-cairo-reference.png | Bin 3203 -> 3197 bytes ...mbolizer-1-501-100-2.0-cairo-reference.png | Bin 3201 -> 3187 bytes ...mbolizer-1-502-100-2.0-cairo-reference.png | Bin 3220 -> 3219 bytes ...e-centroid-600-400-1.0-cairo-reference.png | Bin 6938 -> 6886 bytes 54 files changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/visual_tests/images/collision-600-400-1.0-cairo-reference.png b/tests/visual_tests/images/collision-600-400-1.0-cairo-reference.png index 4780ab9944516953ac09b623875755416662c00c..c8a3eba8db1b17549aac573eb0139a294abc1989 100644 GIT binary patch literal 28021 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

50G|H5wga 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 Zr&ZnY;fEHkW diff --git a/tests/visual_tests/images/lines-2-800-800-1.0-cairo-reference.png b/tests/visual_tests/images/lines-2-800-800-1.0-cairo-reference.png index 8732867f1d56f9afecfcb408abcbd21e130fe133..8a11d0236afa603332c459eb27c79fa37803e456 100644 GIT binary patch delta 15600 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 delta 15565 zcmZ|0bwE^Y_wRiVAu19Ak^+NtOG}S*NJ!@(EiK(_6s2QmkW#vb8jx1Hk#11ByBU}> z-uL}J=XuXLzxi+Wo_+0g)mrPbuJ7zcfqq1RU<@FjlCc|vK}Q$A4|?`eN(v<_i&9cT zsj8whH2NJJP}f^_FRSa6(l9K-ZepF>8sINAzDj7!1k^VzE0^ z1*(QZHl-ZVuGD#o8P^S!?!@dCxn)o0ccY96d|Q1urqY!J|fk8wQYFrA9JhWl}Yay_`mK;E%04Mev4 zjMf%Cx|+^O7<~*}>&0N>qW2X7uq>0|m5cgnSRJ~se(VpqgfKBy&VAxmDg6gMlZX&% z;$<+{d%1A5vFUs$TuQfk)1H&NM%R;Xa&@barIaOZ!7;`lzc(j&tJIbBTT`=ADsU{N z3lnJE)fTfVE$Mo#x7b!59r>sX{;J!mrZT<14sn2PP{R~Ac!JC6eKnmrJ@xt(i~#F% z#4g=pNzaB;an;rB`%B(C5ujoOT7GK#D51Ya1E_BPu#r_s$-GO7tL6p-gx{FE%n8o3(^60?5p8rgRVYm zWJQVc`7t!WN~9w^@ikT+K0;T4D2HE>PG^lI#NIM7*1Vc*MhP|hmVviPqT-zxuVl;6 zIE}8(6i(93)IH|OYwk$<^aj|;MH3kv_-b?q&YEQJNANqbv%lDdbbSQ)HP6^|9MK%X z1NNclioT|53ds$e?C0Wjm9Zt5+ZL&p{}1mrhw{%s2tR_HbGG6Q zNrSv37z4XKa&4Pj5=S3M&ywi2VlJ2|CL+x1=B3Kavf}ICJxz73_l@_Z!idw2(l9JN zzEG4H(ca3%SSBgh%Xk3Vil-cpi)dfDeauLpdyh$#ORakFJr5^%1BWTv4m3laPTsDl zAc9^SuZMs7FI%*6HOQ=a9-&I>zR2xNyopGPPK@n@=JU{jOKSiv(T$}BB5FH6$IPi( zvxT&8MCqhh3~EaL4qx|~48tmqg_IZ7J@e+v&mJnMI8fVe>7~ZSmA3@a^MGF#HB`gr zmQqfHs)jAa!0@GyZpgt6hJ8E94dbjQ@YZw5%b3=i1P1?e93Oa^$xY3vaG6`p0KuhK z*3J9V3j_G@Egl!@2y+W(`>(BPAS&Y%uAfI!G_?5%29~Y`rSFmid6B^$zgV%-NEusqh zukm`Kck2RE9H?Mrc7{`Nr{jPOu-7v=c`i$1Rz*<<5MYiPK;Odm*Tuy$o_s2|8e!Kn zP^Ub-u8GUGyLIpHD0XxqP)*92dJZ)>-8+iG+NX?}se(Rp!HWxR;>NO?$y|im|k*MdQNq zZ(qJO0G|GA&V!j*4bQWFp0pvmcnwo0AMXA-$ko@It_>B`mBt-R)&Qn*I-uv=4Uf?@@9c$2yeN^OxQd5n|!>|u~+Jdc;Z&+wD`p!?n z@eZf1f}+k{@^sC=7@a3xr|ms)J`qeDl>_V(*)a$4S4g6An4^Ch{`?+6jS$#ei=Yg0 zk6Ws!$rpk*i`Ml69VV{)yj}8P1LY>rG?Qfkz;Q3G5S5IW+NpjlaXP!s+xX7e-^$C> zQt9*wD2z&WtL0ypkZP#eJ??X;QQ}&StHjFleD(%O>T4Th-15A&lq*O?5s=6#`;;10 zs*^c>ZUX6}_HUZ(g{~)lKw9!F!IF{>qdi#Zz#|yisuK9@V-FK_d33bQajQj=4b(+D z!bxLP|BQbWNrK2Yluj{nwuAU>W?g*(uh}QQ0rooY@XC?+$afW?F+UkDHrF~AbBbR) zH8eO$(_J*y)9{RX29Es=oW1{ahZyO(8aX4z&A!XZGI$K1k`q)lepB6lqE_`IvFOv0 zdmfyHP%@nNSKs#acNf&`uMPtgw&km`?za~1GTe;!pp(ACoE2kaGkukaN=C~M+eTM@ zR_lV3p1Ptmvn{v^DY}0R8Mif6Z5A$5>AIV5zq%g^1>2YGtl~eh1=?}jj5U(y+roEL zKw+wCSxS^Aype9>OTWY!uxp?mv8nVLzVl5hO01e%_*3D7n&a|nf>I%cH|1>2O$-?1 zum*EwJD-!;M%M-m`BX|+uXgPViB8fDW_o)lL8zC%)muNW&knEBGov4cWxm>Cf7lU| z{pi2^9q<6%k&wy+@^4=+T133uVHe{4T+v|YqL^^gi6HX;iUwaN((g8*1@OLr#}RigJ12(+dB%Nu;{`o_0rj_XRGe z^GP)OleA^f3CS6HQoi@=9KC$m+(ZO4lkzb8+o(7}eLeSZ7>YQ)U|C#0P0L7M*LGch zDw=~M6pCaGT5^S<_u_FRa090nTD)f`*-Xa!EE4y>7Y&Q>)d-Q32oQ_UZ&6FyD$3td6;os zO=g(sG>aptgs^Yk-HFTe!4s?v8dwa@SOL&y7vrQ`nx}opzzEup?p;ZO`jE?fMs+&s zXfYoCnitQVUG%jE2Y*!lkd=m%G+h2}XZ9;D% zIrYV%0tKOt0i~d7ai1|c;YOEba9b}1+U!TYkLM1$J_dFSik2skSIRuia_?uwh_k?& z)YWr}=o%_9(kbNLjNmN08tg9D4YAx?o?{vVVp7zig#b;cw~An&Kzp8%<$xZ+m|J%9 zuo~}UsDj4JaGl}A6MfRVpVFB8skIN3v zfltPJ?FKImC2-{-$9B!(+gCBIiFbm4C(8?;jQr^ATNaR@^MWi(5iseyi=oC|AK*@YTmfi0QCGv!eU(bO z8;N}@CSmR;fsoq0qriFp-AeAhtg8!uAvnq1#s+P~9)u8PG`#}yes@9Ogv=CCnkhL^ z+9z=Gv-djNr}8$0$#+8u?n89w;+-)U0gv^4+6j~mS@eO@IZ-M5ZQ%UP;N4je{Px*k z9Drgy17<$~N3AkbOMZf;f2~fHy~0_=tOW!VGi;LVD|kr%6G5TAZCc<~CPD%59 zIi%T-!BP6dUFI3_g=D8 z_8Z^C!2<&FaQT9&Tl6MJy5GIYX*SxLAZJPT&%3kom9<}t<$;RvHU@Y*1cXf?>k-P? zcP5ii_^{mNPrJ1K-L}rN)Zd>HSx0oS7#vCRZf4sCWEU%4EFD#-U9+Sh9uNYB`ka_4 z5n!gjNdwl(vA#jL#@Li&taM+Y29g*b)81Euv8EmQWMaClbSRH>9KSNVGw073IMZYQ zn*|FVVQrs~g2Eob2{=ySQF22ucsP3zO6>I{}GrIS3 zmVR3#fo-0iqWv{@yMp{J-lDVyWXF;$Rrnb~euEQ(x4w_GyI^`5Y4LGz2lN;|{3wb* ztvV`fY%G>;B%bwO1JYJjy4MS)2Z%%kzDCxo0d!!|FW3wW0^ueC_dv>>)=GdTu~YzCfNLEQt{5OlZl5AX)kXw=4z4BnPdMX14e?cpH++&;&LE z;spE0xda$(@sFzrchbmk|6~>*FCgK1dpKSlj5+ASO%40=#$Tt0(dbc;*`W3|d;8Y; z&GKqh;qSB{^Pq^}KqKmO@)db4`mtR@yNv=toUUw8J&_PP{v*G4ICFxjwo`71(L3N; zI8=H3*Ww3C=>@ms1G6foL0c-`&gl5*7i3I|FXdpTv%Zb!!{<1ra*Ybefp#0Jb`*TTmh7mc!Bzk1vWE z-@FDB8GOIjbY6&B>V7FTK(|Lo%Cej@n^uF75x1$!NImi}gn(2j+5+_VhIH^FjfR*( zme=t_VQ6Or+`c|+MNM(Xo!t&BY0}?5+fqnPpF-Wq77L55HvRU<$2>0dS@{vvxD>m% z;Cb?)h87H-`*t&JZjvB*a&Y4ah0$PHhdBph3Ea@+5w4>lUq46dumWKYpYu=+`Oa~fy1T5_f*=H)1IvNt0&{?; zj~F$wlWv6i`02VN(1@QX(oc0$Lop%1Qu1S7(`1I)-Ll-p$u~=W!=YRU$IHMU2T`Z( z`;(q&q+nKAU@oryLV5*Smj=1!$@fbDA!QW*VO)dHO; zRr`p9JV^029b7sJT zL*!e`F3QsA7j-stU7>eBEeLfu+1ocxp!}YxNPEW~)}cWg%OD{r%5Z_`#Zy9ckrus9 zI3)4VW)}lAKp1fufrpYlPL)9h3Dnpf=(Hb|@*AS4?{r)>bHC?tFMAkXwZSKU4XT}* z`UzvXh{=;ec`eiCXsQO0nmBR1B1|H-$~r!22wUG3yrNm^LFm{M9n4X>B zGoe&sp$u;vkFMx0yExIM%%9O>Xk4Hn6#~Jzok{zay=VJzGy1asj#XD6`9IJFe49;g znO&~9RX<%gWb3kBqd76OFijRu=Ku%C6=967aBQ1(1zzUSR1zPugC2ygh6RrIZO2+R zj}sZg<>Sv*aSsZ4IRgA3m(edf3Z^_@-n&ihd&J#(bYbvZ30<4ZFHve$!}^r?Gvc!( zpRb7ES>zPWW=8C|xa+NmYY|?6CqNb1@*I9TuV-Z=T+dI7glKI}(=V&=O% zF@}O6c#if8p<_Zmxu{|i#o4jD|E;cv zEa*Tl4yyx7(<%roV-j(=oRiDj#QCH*1$Gl_VT@{OljvY%4_9YQSs?5a$CIZu^=F>c zzS$+`rvtyV9RA5s6yolG{97)0+W6^j@wrvDwTcLY5*|%sEv12FOHNp1L zU~9+p_WbUL^KBwjUW=8z$3CSY%?5SyRH6%CyArEMfa4wz2Te!w=dders_~LR%8dFt zp|CiIRfR+YPemseJhIZRy&s9-)va}Qi7;+tXz0?VH(y@r`=x!vJ1RNIbUjR+_GTrb zX<&f=Kv<~Ub5Lz?@JO7(ynA|7ze|pJV`lQG-dm8jZzxl=S%k9w#W#WP^niz;{@|cK z1j+fsM-i}xm!z9jYwPsJly&Ymq|g@XzGz?Oer-d!DU=kIsP1*8aE2|B)lTy!>RL|Y zfr8uTZ8{w{j5S83J;n4-BI1?qR@$AWI{Uw*8!eplw#+9v+)os-IvQDBNaw;0_2F*M zMYP~kAZWH?mFSDLryWDu+l}(5dT6gz8A1&%F`lQ`8q*fN{g1fSlJwE>y zD7|Dd6s{GlC82=Qrlim5=~_Aam|rU80_WqSH43R3S8DRt)>eXE_tw2y%SsHFEn6vJ z%pv7PmQ2C5a&ki|k0b;nhPvl=V7GO878y%F?jD+RUp1mVY8 zm8mNck(?Py2ClB8gfE|FesplpnExsT1f-W}nYLIW!suyrjBK8=e2TKnVbsb4k;ZkF zhoJWGMG=`wknAxn46~HL7dsY1AN4mAzo-?>E|}^Iue&5aFJON&>mT}=q4-spOWdBF z?Xj?uw0xd)a9!b~U8VjyHf8-6of_8f<8jSqoF7kr;H?KH*uEFQ%9XdZaruY{1S$-C zxd{~9GEIBiefz5z>KpOIOYqp!hDS^^IcCNwGYJU;C@0!Q5>MU*5G}Ktmx8~fe>mJq zP+;8Nen9qC@j>#F@LbvGDB2}4K7I*5pK#70HMwczhjS&Q2p#lj|#Pnm_f^?E5`c_j@``?_#J;4AMoHhPkMT zZwi>bbJZ9`zxSC!W_=oMWis%OB#YjdLmSC8jJw!lst^+D6q3)cd3B_K&S6W#j>lLl zz;&SVP7#(&Rz4-{^1(M^tFOnT;aQv-$_mZ4nk#SqE(g5vBgoJ&1``&pyDOEoi67H3 z`DO>1pIs|dNpm193=Nx!U=|^I)Y`UEk3gnt?s?FBoLs~6Fli3|%mpz_6%EZ_3Z8Dq z*#%P-RoV`+*nRa0lmIL!ZdCJu0+|U0oWsM22FLZpL3)jDjpQR-qF&MVr%r8cM0tE? zWpZIG`wAZtX=eFZ7rDhKi1^Y>)S56AIS5yQHt~x!f5nnXFwD@ncY?A$T}F0)E1cVy z-d&zN{*rRuc9_cNcQRa?X+*syFI&=dtjZxnP^ohz7~tOK1%x6d0a?7BHD>rxU`6bE zZ;8ch4J+*fgU9kdW*Xhovo>9-M#eN$Z|p4&`>uPPJ&s4Ww0SJ5QM}4*)}SpCL&ktd zV+HUFJxhFcvL~q}ya8`cElWR!(=OCRxc;no`sU2tV3+|ErlK}^0X{m>6-}(c&Kr_I z_-KVUYp|%(0kZ5An$ZFzcs@@&l6XB$-MDk7cG&8L^da*e2n*@HK(}CaOG!SQ4sK=Y zk-)>8K0z8n|IER@a-}T48td;BRRU4Sn$RtQg%TrH(||Rt_PvKC%pNKljRT%uQCw#v zxyI+Lx>aU5M?`(K(nZD0g7=uatYkY|d7lecywmn)1OhlCrjP^XX0B$z2UOXhnWyB$ zLAv+4?~x&v#lW}3n91djUZtl~(WbTZ&_7ef?pn_xmk=J#_v&3^mJ1%L>LHevwpGf% z*Bvq>i^)6GIC-6X^)vN>ggVyhavfJXg@|}=ddi{rhkf^)u>LYWt z(EBINT|ixumG#U7@zf=(k~t(e_|+5*fBmETDCLtqFPozCkO=K{B4>;_PFRN)8?_zbIKEuNJ9_J0 zV-8HUMJ6rHR$hz&MaIf2>?QGOk)1Ksy>f!tox1t(uoX(E{Rx{gp=53S(xls{tE~iB z1Sl$-06EarWox6hRpBBV{NU0^)YZS0oDucZE0emqeuol^Bg`Cbb4HRuWWvq6uM5uK zwSPq}@(AHDfYI>L!Sqh1SYp>Ryd=}PTHVW1*Eh?gS70drS>=Kw{=Ls^49<Yni`_02^Srxj!S2V18_8!Z0V zakbXf%4WnwwX7TK68iJ|Hm{`%A}7tc580hq36aULoS&rMRF9MwIurry9g&#aE$*87`#u+`9Xz z0wP|XU+Z**1!|vY+Y|tgi(y~yRm<_1)GsS6TQ15eE`dI=uLcTMu`5D+a1$4#r0Hm( zvu%-}*S-xR1MG9`>#W+v#G}!P4gH-i<0bWl@!N>H0uQZFW3$HE4Xu5m;v8(oh1gZ# z_buBBNU@vtz>j=tV?U#j_1-qR2j4s;QS^uv$y)!mkr(0)rNGB4)Zy^uspL4>n(^($ z?OuM_<>8R8O(HSlQgd1WkLmw2%;e$~JABBWc=mAPf3+jF1cl@SmD6V~yazioyG z9EG5cvLvtj$cSxa9y%TR98t5%cK!nOX2mRBB3CFrk1xOjBo->9tn@j-O%2WffcFW^eBec+~xMPb$)xchTKvgzn10kN?lA6-wVF8@AmBKe#9+Uhc=6|SB^v1;z zkxFQ+W>@U8b>f>~Mc6S{Qm$(#vw1}GE{Vog(|6M6w%G@C#5>EVBO5ck$8p3TBCEE> zJ{1#Q6y|s<3B=>rNzbY;78qQqUa7_u1huMv=D6=IW6lH^hbU#NMfuN0Zc`?lne7+B zZ=)M{b?fVzhjUH@bxSbC$|Ra&J7TY~y7T7|}aCc5UOCR9cqh)Fr* zS#yJf*LO8%^&!y-{T?B^_KymC@}WdF&|ssd(FNpNF#Q0VDw=@O}D$SvEH5{6A{8sEH}S+gtk zoDAudntqB55t>vrjUO*|7i1}$rFrZi_7Yknl5a#!6G2k_t6j^9;edhW?+1ERt-q@0BL|v_f>M3h3M+ zQjF&w=IokJvK`9)M@B@Lpc&B`@jLA@SS+S*n=3c}%P^M*dkf~LXCJc})kd%konj5` zm>38b#;G+703yM;*=&<;yagLRHSdJAz{0_q>c8uZxCa|sr7Ui&dp7s}2Rvk+eNk`f zQ?~Z$eA`iz`-h3m=ET3Gh>Ipgy792OWHONz!=Q3b{5b;IrOdYvt9AByTCGVvX4-o( z%ve&{z~aDAHkZ|-P?MT;;)Giz2*xw-XJAB?PoX|HLPqy=#6tXP)>Er8SxM4 zv%Wd4Y488m%j+^F^Uxkg#%8GH-@>UM^Dq1RNtCKJec=BP;$Y=Jgs6YtVOcinN5ePs4rp4; zM8rM5#JVY6b6g%@e)x#b!go&&MGP};>m%6}jQ;?kjJW|#40;V|zt(#YDlzN^GT|)uIRKS!rV%aA|fNpurW7g zh|45p%7yq34A%TN433aG38^|(+9d;#oQvg;*f>4pt_x;lQ1#}-_I|O+sj2JY8(nAR zS4H*0d6t4@gQvaTweEh7nNg8d)f=ktXgMx?$FL-sVL2+ufE6Tra7I_SQ>VBzj#eq6 z*_B6<3;r&+GaSy3cS_aYrBpb9(5b1~IuYIlO%^&4n~$-39*#6vz`yEpfPoR{9~2}- zqoBKz5qfmZ4qj{9i>%osHC&N{j?Tl=J8PG9CfTVY@h5iNAvSiC`@a%g2}N~_k{>z0 zqTPLt)1mW}S0&Us`$und-%Cy~HPSB-he2c*S|nFC9necX#n*5y`c&hkx4CC{;795w z%NdO_qM0hYq4RS?YEt+>Z{KVE=G95vPm>V(9p{9@m$8~^QN!>k`}EIyCR9 z>QL&91*(GePCICbHFw76bOXI7Wi!VbvA@sWV~>2T@naVu?V9S;bIqw75&Cmv%7fTy z8>4F1eRf-2kd#kWqa~yI|KvX#2OyuoAu2LGjoi=HFFmGihL&PZgfG`4EHC|DIV7HnBo;`G}$c(R>jKWal$!)w9AK1tB+|$Am}zPwTZ}9 zE85B5v9iqWOJHH1X~}2G%Hem@a=;(cf(I$#4z|c~6+Urz_#9yYU-bIQ`|Zb5mgPE>{ zx9cHBA8Y8_9UZpUQ*yvJh_ceRQuWg4p30+@H0$855H+>R&a2-0X(m6RVJ6HSb}g9R zwtgquhxa7o^j|BLRdqU#RPqb+<(&#Uv6M#*^=Dd=bvk}97H#<Cg>)4&j8E9MbqY_ZI8`9nalv02f~+NV3&$1uys zu!wdft!PWl3(uA;V-Y4RBty)3*fgt_AZE6ZHSIF+@&~D%`AK1wyA=-G3XUAQs#JJc zWEU~|Ss0~^W8_0USwN-%uNi+~)@&e)r#R{5gXEZhfcsUYNgZh)Yw+l5GhrL!JwyPm zY5Qb9mVjr5m>PImG9@2JZC1oMH@mG_>o)hA@oJEG`w#amzLm~gs8V}swso?*PC;0< zOtTfO3OzlgoNOsBD}>Zbe7U#!CHE9vYU)h>&K^OX-2G$TPgK*JC0EdKFFx&5ZVhoR z>0Ol|DCVtVM3E)TOzE=z#NQ`Oh4U@EgL_TD{KPJv4B*C7bK1-ss+>{G>pd+<8m}GG zEl5~KqA2{`?#$DtoOxnC{pPd2T+eb4uNPta?6#ihX-4N0is-95Dk1KYE=An2+d{Y` zy;W*bC3v3?cN=Q;m2MleFNdnV+9o#?9h?p;={LuWjCussmlPgSg^Dz@1>2vSMBjE+ zQxG-_12+=>0^P;7diB9QMv4FA=UT`&`Nruk!E1k&z$3DtS=Y>EZe2cSG{57p83g6^ zmx4ODzs?q|@{rr*C$V-@hTm&`D5{*=_`G3kWXC;mK8*wHSk`NqUFL=;a?^w$tHvMO zZPm7XiNcY|vaYTw8D*y3qj+BGuR<@$X$JVHT(BjprkDs%ylZURPKCygbA3*7bSo+V zXO|f|GqMX(a(}fCNnrCaGk<4hucLL!jv0@Mt2?2)JXPg2ILyx^$ZT~PAdTCT_&zMY zKU-S3LnDs&Ia4_NQ8XTxZB9gcW!QUVG8_ux0u1vLwMSX$)|yQn$Her~*7~bqU+Dk= zM$MoV>5Xg<#fGunZNJ~<02VC^hRcy5+0dg58V%J~mzmqAig`O7^M_u5Oh;gbh=5CQ zC9_Pz6?DZ=xw?9HJIv0nXF6V5TKZZblmkb`;j+Gl-@Y^;yErNIWWuqY*Yt0e&aMjk z6w$OC3MOP3FM-4S+BiSoJAe_9N$qJ3o$f@D-L~Vq z(+>s}8jm0nPlBE!MpmKCoYv4Ie5U4sohcUuEExy31rk_GxPi3h&;J|y_DT*0{u}$O z7yobU1OCH(#*Aq0EB|lq3+@_?uSqK>S+by~8W};eA7i~ty8XCbYuVNeLwjdn$w)bx z`b_Jxlb6c#T@k`_px2+P36v%>{1@)zq_x+8fkm#mvbauyznPc+gm}CClSem5_dTDF zq?ll{RWH?cIgl{76^NCaUmHEm5v?#0a;xf0!S9I9o%+kqC0gI|A}dNW>TwXR+LDso zu+Kq1)-^_Qbc*fp+y$S6?RmI9|EWb6Hb=eOBmW$FSe9sO=+h8{6B=0fU_?$LJ-5s) zi^0|(p|$i_#E1?g z3Aru7?p`CWYfq!7gj%i&kQJ+HEzT=Dz%!rQ&a4w!lVpag9h2+hsLS^w$-zWzt^U8^ zPV>O!loP5w3b^dv`zJw1gV{H z->Ec(I2AXyAd3J^vaa*wB|>=v;5g5T-AOwVxdlh<;Ejx(7nt1TU*Bn?O5tH|74;+Y z&XCqy@Ch`GWL+k&I@!_#zp4CAseo-;71XKR2#=?E2YYa@XiM$y$~J8g=$9>%OA zQ0_cxTm9P~?_`X#P)Nqgr$AmyT5H0c67?&BtHVXmab$Vt+J2mqEafu_Wm&Ao3KEO`G1&kVhhcTb>M?r&;51mnF>iv3>aPfYb>H|)mal8k=kOPOR{+wZB{}#*AY`cdY;;UX`m?0(;jjG5 z+qOMF?$@^W@2^hYwEE~c*_>bB`c6%h1NDgqol4lC_fM}^5eHOrW-Vt*H7AYmh2e`f z#!rEe^7+==Wp`KeHEuC;FVn$LFc=0<)gg!p+RoClR()BC(HAfe=KVYx-3p;qv)W6| zG_V_|7OnvOOWyl_$J-a9${}rLy8ah#kc6Pwf~(k_G*a=vPdFDQsJrv8&l^qi1{Y}X zd**~DG@FEI#k?|6KI@6TSI}pn@m>a*nFof<&wK^Iv`s_{4ye-~hPK^jX#nk-7wdj= za}?^tWG+#?KUb5>pHw&+Kuy_XdNCX!500BpKZ{eqzLjiUILe5+`bB$+c4pP(ln-d{ zJ6I3cslCoF5Saa=zqB42c4;7ryIDnS)EN#~180-{MV`-xn;pY~*2T$Tb~*t&9%?j!VYLfGqzBjK?!} zAn%(s2YCL*;Ztr|h*HE&2VDIcL}zcTyczqGU3C%j_;It}?zOU$Z2uy_dG=m!pWrj` zZc!jP%?jPxZf_sTkxNsqA%C&A3NZ2Vi% zxx3xd^YgmT&AyGQAK86XgnM#&Gwcg-jElHHIt^Z69-z(}S^L+;pZPL_otbHBHe6gM zgk&LX_CG3S*Z!4ZiqRgwhq%UyclbWJLaO}HTYgCz8GdPI`R@Unpinn(LB_6mq8uq} zHN_1dRKw>E4KyNMXxg~8XV^lXP{VvX@=6X`;&%CuCR=l9R`NVu?(RK0SAgs_Zis9 z0Y$D>)hc(MwX5j8+$~u_8P}P&X0O!FZ_;0Mtw-O zkG1x>$Y@~8yk%$GoKPJt>DN=RB{3GjFVn3G?C9%z=zQEF(lz{&bfaLna;Dd&mJ%;? zQ`nWOv-IRCY$}d(3>WUyDi5IYNSs(=gk(8rV7_ybg@*%!OZlol&yuTs%s)Q7ZwDmI znkBZV9fy@GSyTS4wWj+=YmNKAwbuVA_87$<^g2QICF3g~!v(SUoy>5Cg&X9HM&(zP zZV*RQLUCZ*8JSbW!ofjknucWmvM@9}u+Vx_qKIB+D>VXT-Ha_@z}=2m_!w9*sl+#6Xp<5P*G8tG-t+tbRhHlw*_%afhp%3YD@D+?e!Nd^O?Y+3nM17_v0@b9pbt9W(AUVc8e%c=J}ZFJ$T1yKyV_DB3wnvbs^=uEE*h z>Ka^biFy3e3{UNjTn!+qi#It#>?X(W(Q{MkUY0bbC2hr)C-YVNiCWDBc1RNTsfl?! zKTqo|4_xS(ZTrwwqwnie*SJf@CtMj~F%jrzCNUSlGhySc)LVYNe%p?Hv)JD?(Y!-! zH|CHpChll95tu@A|8`4yV%PIkqpxXlqu=xpbH4vwxf75^V*p$!GLgR9;GiTt!Td_# z`xqJYEk+EbESLP42-)|omhau<8I)|CAw*-Y`_@%uS zDM1{sB$4K0f{y#)xTl9Ufr}q_lfUW5uLd#MtOI z&kcU&IzYG21J1{hYE7Q${p}oI+PiC|;yV0d#7_)xH0um(g8im+Xktj}RuejK&7WIr zTq@m)%xW0wMt2)+_wt1&Uxn(cI(X?+&y+;$*5K!^Q&6W6pWsZ5{&R4?lWk8&NbJ(^ zDHdJq)=5_Cbg9j&VJ#%TdAv&68HQQ$P$N?2eV3P8dMmnlr~F~S%7xv+=Z@#XfkB@e z0Ou1Vp_u67`NhA*^{+psq1ONNd}IC;-;3X;dR*XqdFgU%s|eH2R*Mn~P4H9+BUtL- z=OHg&MorzAG3)w3)p4o2@JElb>u5!7v6>TDjmM| z8)+RuJIb*Yrql)DydSn89)I=ija1-s@s@LZq{Nw6et zS_c=c?~D!Jq-=eiixcRf*%GTo{U%4r-nWphg<8n{WUJtz+o(vCZflf zs_UU0_NuSG;TY+q+sR$xUZR}{pFhvZSb-iJdgjJ>k*`8|eCt~mYKFy=*AmmZV^06v zfJ+Y?7P;L?E;Pkh`mTMJ7>%FnRw*SR9Mcp^ZTaQUFqM{!9(41WmSpROn8Z0J7(xT` zO69AypQvdV_t+9V61chJnD6H^&KusFLg}-BXMDk0rq@GwenCc7mfmYtTkU#V73z>k z&&KLQfrg|@-rL*0(~+aJ)J&f)N%ICw(ubi(P-rmieQk2d(fFM?l^dvzq)6ruof3}$ zDG-nNH9X~GQ+eex7 z{Bl!?I@)WQD9)%t_b<7T4>fY+tAG8Rl;u@g)}z{cHrEP1``p8ZY~Y#O{AGK8a$FL9 zpLvFrG96$KmWL5H;p=@dLT>0`x#3Ja7E*cqZf`^b)N9*5oxecb(k^mRb^J|nQOIuc zp@N{Kh@&myA*YIECVJhx@^=$ENE5KbW{9Ar9)4S*4SOY+xz>AQHE00pU6${TGAU1p zfsX)KDYY+Cy_1+xX#x@040hT#Zky|-zw8)IAI4zik2e3TZQ+iK3^;lDA%P zW1@}Nz{zvd!By~}>>4^Q@P29T!(nmZ*<4nkUu^_=RhPzl_ARVvg01FDyg)wQNVz0X zZyGlEc?}~jD!6iRu<=)fe08@MkN*_&ME;;bOG{C&3y5dIvG3;!Q$E4TvhW25uQ*{t zKiqrRsziIM8VY%J5zOndH8ik0koerI7&TVt z^i@#v1z>&6+vyi@Hx`aIA3S?jkfu_m^Bea`^`PI|T!$o&+S#Qm%Xxm@D}a$z)Uq?s zqQ-PrEWq_HXDgW_>|jCZB6?CD9>8t7>*qMhL4lstNC#U4nzl}<;)7d5NmXO@*qwVXm-YC;VRdQKV{l-%^aJy5?NPd~YV(QN z@{;;R(|N+Vs8Ia8K$x;-rjQ*v?gd$}?_UqrIF&5{z})zj9c~~ zHkX{)?=mf300hx99^%_Na}H)J+TmWT?GlhLxz~Zr@L?Jz7K&c1zp@Xn{0Uabgz5Tg zULGBk#zy0i;`iK)3qy~&16nT(m$$Q@cud6<&vh&@_L^L{vl~0s*h6TSzs3~jnI z>eKeQU5ladJDxpZ4YA9-eNz5Lzl|B5+f?hjl9Z?__K1U)aNkn~t5&6ZOtO0<(Vao^ zEfBD;y|dV{fD7_I{Ka|fEZC-2<=cF?c3>FSIpnIczZ5R|%&Tkm=A!vA>~!d8`S!(5 z<6g+1Ly~?&m$=tE(b~DzySRyq17Ak)srC}X-5TQQZtY`ONX`Oz?;WZA#kne^$F=E2 z{j38$m|&-jEb|tT7=x8_t^w%~UaTc^13D39=?en#kn`r|CoukJ$5LFhT97~ANMShn zu1#L`0g*VMnTzu`=rQ#YwdovaHy69#(^AKb8Mj*O6d++76u9f_%MT`?@q0I>PJev1 zMTupxG*=A@LnpX&fbQ61n7-J(83F diff --git a/tests/visual_tests/images/lines-2-800-800-2.0-cairo-reference.png b/tests/visual_tests/images/lines-2-800-800-2.0-cairo-reference.png index 2794e90cf36d1b62484fd9e5f0f41fdac137638a..e4cd18585cf0cf74eb6f5f97329c4d4f47fc2d6c 100644 GIT binary patch literal 10793 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 literal 10826 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 delta 1703 zcmV;Y23YyJ555nOBmw!6C69j+68{z!{~8+q9UcE6A^#~U|1K{7Ffji)IsZXH|3yXr zOice#QU6?A|6*eQYHI&(ZvS(0|9yS`fr0;vi~p08|DB!xqN4w;t^cyJ%*@RHySviT z(*MN7|IEz))6@Uk+yCU`|LExd?(YBp|EvU>Bme*gfJsC_RCt{2n+ZKv;yM(73$lzj zh#P`~0wOS2WN>hF*jxYscPan>$GnuSN!z3??Hezg`<)}SImsnolBQ`8X}l&#UtIyO zlY9p#lZ^)ve~on%QASJh{V-h@7a0xmX~$Uy$YFy?iVX3;VXW_osPC z!YofdzPS-elxBG%SvegE?vmz^3;Ur!%;8}4LL}zTf1b}oHa3qk2hu6=@NH*K!oea* z3MbRYyZ&@KqtBC$r%zMC*d!^HiTRSz^oe>}2t?D_J)biZFGTVoX(N^@sFjqm#e z$c2bo!IvOOX_*;XTAm46DL+jrN+Gp#q@ujLskTdW=bwwnrS!o!$xm85;Zsj{WAZss zAZ%}ue@dR~MCN%&-P1>jLn$*$3LcP`Qz>`ukLL-8fqyTek_&H?W*7T@lr(QjJvs=@ zl4y~69{JQCIf%zek4=-phiVupSCo7c{9hv?r=*-#$&!>*k9IaiBAa{UAa4*UNk>b3 zycZ(VD?OqWM<3Z+cQD3X=-^XG+h%Du>A)e%3!5@DxEj)Lm>ew4f%`6VfMHczU? z15#*~dy(fSDHl4E&SpvBALQky^s`z$Qv(K%9aZANOvEvtPfv)B>9hW$Svf8Yp$XAc|Ws`gd z9e>ta)-VFc}zv@VECv0|BH{#6>0GDXgqGpa;;}wz>&zXdGSf!nq9un z;MNYG;?Gg~_40J+^u`~P_gc@Y3GIsvTNQb-Yy2$B7k9j>+}ax2@0`*TUfY#L#+z|_ zc2w<%Rcwm9-Ca)OU5&?e^}XCOuZ(+<(SHS<^SQh@EVn*fiYUDsst)gqxMtOHFVbOh zR_T*Vmm+OC*nLYoZoGOgqWlt+dv+!AS*BxEnX`dDvulyAs9ejHi0GTFrarT4k>8?n z7yJv}T12S`BA4}%-HJSl3--AMtwlP*$ogBATahbq!TRzdN>v#7##<4?KT(%;r+?gA zsilb3??rWUyGxO4Cfil_bJSsHu`aKqrAW=9P8}~rp6N`#rjxtUp)bU8AA&sXp4w1Jh#A3DhGXp%Ny5{);-rFml-ZDg8!&nasmGGzLC z-jMHf>a8~tDlIy$_QPmqD*MeTYkwhfLx1%Cd zGwX<NNF+}wUv)FquFDn-Lpd&Q2^btM18}iIQbEe|&Sqv84=)kp;2YTkD-m`T_oqMKsL^<{h~Nl? zd0;ofU7=X7wRBIOe;T^=D7UYdqbUn<@jTKNN7mn;niqE9Kj;mGA6Dbn(^BMB7}?O* z4GZrnf-Nf1-h<&W&WVV{I)7fj``}vSPEgFUMpydWu0=#6fcsQsOXNckS$_0f zWZdBnu8OFbjqRXoUSPAnb}uqkx|gPfYKB&GDDuoVG4{0wB7{f7W=f?k*k!aSLR9Kz zwUNtpV}-mCA(sjrNPWV#!tQRo@kVvi8<}MEN`!o5Yj*3c_w8i2{Yw4F=N-LzxbAT_ zn7k7q4+})4_F!{O>fjqIWj>!=-t&WdDMDsU`wKZoU%ja+<)&c=p2k}d2RQvQbYN8n x#STkXm(M!>T4A+8lYIv#lZ^)y8OGX({0Gr`=_OaW$l?G1002ovPDHLkV1hrHTD$-N diff --git a/tests/visual_tests/images/lines-3-200-200-2.0-cairo-reference.png b/tests/visual_tests/images/lines-3-200-200-2.0-cairo-reference.png index d97b38ca2816073d8a03efda61bd79ed6ea66314..81727a68e5120b167465e0f068c8373e2dd6960f 100644 GIT binary patch literal 1149 zcma)+Ycv}M6vtD{BIXF{SlLL6uGOdoT`Gi%h&VE|S~kM4FjI2n0gM zlS)3L(t*7~s;hW4Kcr42$-cC+?tnmmrY2}w}>o7#oABDVUpsr6pjofWrZS z0M5=pp@5ec`1-=Rb3msZb@)VGi}T1iq@k1M>Sg-HAU##NY~;w!d2wGW(YX$>Mw%F73hZ7V&s7Y z*%yWqYR=%2b1iojx$o6duRJGdhQHo(i-D@u=Ay;y|HsG!etU^iIZlsM7MX$CcSz-B zlUk$jv1?`#G-^44eZ|ZmE|?45=q`Rzde)>?2OKw zWC#emcZt#%ytHy>@gybh7f(O-*y`_h`w!mnFrDD!W}plnv?PQjBb!FV^8OHt}`` z!npNB`}INMtmLCWs-%t7>e8FYieoG9G`wH8K%3%VOD;`v=-9d5BzyMGGKaBgIqUN? zlNTQ9z~92@eIs#HawLgS@B^eA_XB; z6`e|bOwha}a(D_u?xH!gOSO12-fa8n@&ax2itW5rt)^g%?!x`N7d>w~zs8`V$ZfRI zLRBlXoj-3~u6Ek`u+V1VI4SW#**!Veg4)>SG(I|8gE1Mhbm)*WdGRkzce51QwOAeD zK&4G4twgLTB;9A_WNgN_ut*8y-E!BQ&%-mMIaFB z0sh`$avggY%8GI}RwUe$OAhgDgbyGPaPT0gs{;xJy1JmR55~rT!GO6r*w_FL2k!3R z;Q^kW;Oh%PK|mxzWF*AJK}rgwrvrro+1XH72&JV^RRy)R(9{GB1~8exX2ZY$OiTce z2YfyV1RxZGSPTFOV-^$yLXjKb?HO^oX??yhoQ>BRNq&G1cM3l-!|5s$1M2%mZ~oAw zNjs+n$8GUchZ3t2O)bP=^TK%20~}qmbX3XFPl6OxCx*m6{??-ryLD~|2`v7gT zpI9~4ZC|Lc*WPw17-%cJAKIXtE0m}GwWb5_s`($=w9P`|k455-s9bs}4OMc+AZ<{R zW9X==!G$&Q|9#9Num00rbY;&fEzW!7DdvjR+D(x*A20uK1_mj2;5yOC!apZvvso@A z)*MXoqZ^)a{LU~(Xj-l-nyq%>+iBhBX`)+|)a10*Un`b)aeAUs(wwbjt1bR8pm$Ts zY@Wxm27@#z78N~6=SziCIk+2@r`{L`NRmrV8T1r<&L;X7eO-L7dBbEZ;R>N{5qmTx zKlJkA^= zEiZgFoX5Hu|6IS1Bcfa!EO#Gc<~;4TqAi8O3|`Fax7odx z@rawd;{~Y;^TBG^k*G28dgtA$ftl!;!%P1f^L=R>8@t z^vN*0n@lZ`>G*u9lK1wm4C&SWdwPbU+VEBYySjX_;>0V0UZSSsxgHJIM2BGsVLDJx z^i_=zP4ioJH)34L&rFKzezanIyYgJ6m#S3vZ>#Dp=B#vcWO?Y0GHuJ){9dDzuC13#nBCwD`_Ti+@!WcUc_ibs1eP7j|Gb1*lLuCf0d>#*G}OOX_qpw_JAD2s@p$SO2ra zQy%`B?8lANRS^kq)Ba1i$*R@Rt-Z%QJQsVrwQe%R)kG6Z?L!Tod6*}$a;CF0`0mcl zk6M6YNWZ{cN#iDkQptBrmr^_#*I%@XF7zm3=OYps!6&c1{KJ#GGc>Cz@(tstVC~CV zrv$G9n_`r_0=W@c9kFFPPu^tDUJj7`P+1?cv$MTTblQ5jPDvt5Z~AU)KXKf} n$0#%Yq;h6in~!qLr$efQbr7uiS+5Vt-wGnYC)k@sNGkac7s4bn diff --git a/tests/visual_tests/images/lines-3-400-400-2.0-cairo-reference.png b/tests/visual_tests/images/lines-3-400-400-2.0-cairo-reference.png index 3c26f266f861dcc34d00d5d23fea2d2253ea9c03..249edc7093a73941b901d2d10185c1dfaaca6234 100644 GIT binary patch delta 3512 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 delta 3456 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 Zi~>mEQ67XT*NyAvm9o~0az%^a{{qazYMB54 literal 10347 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 diff --git a/tests/visual_tests/images/lines-3-600-600-2.0-cairo-reference.png b/tests/visual_tests/images/lines-3-600-600-2.0-cairo-reference.png index e2cba88af929ed66d6f13b903e7b121e3cd94f40..654a636bcb8d3e9f51f5109a23654df2f4755a16 100644 GIT binary patch delta 6413 zcmXw7XIN89w6$DBDT1hg6hY}lKzc`zBE2XfbjYQKCWL_0Tmk7t2ps_t0fA5g1_&)m zl@d@&=!w!o3%$3z-22`;Kjz!>&78g0%voox`R1$2rSf~;tHG&!Ww0AJs>Q{vtgOg1 zG-P^uG7}S-g@yd!ft#Bf`Qbw{H}|N50-2AGEF?r06C*!;x~!%~mX#$vd$y;pPF7MP zKYvcv(jprez~8+io0%mhCX&s~YhbWEN?>D?o}Ny2cFxPoBfGnoW@eJTy~)15sE-oflR!($w_V@Rb z>+8vA^w`)KxwCU>YKlBCKp+svKYx;Oxc#{~B9Ta*nPji9 zVS(WU;-BxCCB+ccwzMI`v_LxUZ|ng#K4#ZDUVC*F{`#63EUioNR9K`2Mm{6B8hw_y zC&Wf@xw8(fvOko|Vqsw(U{}icee`@dZ(S+1!(`EIcK9VClB!1C{m)N($rpl^?Cg0L z-cs@Rl-PpeQA(TCzTkl1K{EN9y|js-icd4aOZ$C<4v!8}*wuie=Gfw54d3IrqcO#$ zm7<*tvOR2t^>r@$sJDH3QM8fr>EDS48KB}2%hdm$f_;MzII81LZyA8VU9$pbGVNvb zONVQ?qA&p5L{0YSwCIN^d;7iCIB1~=NrCpw7M_HeI4Svi-tyOm9)?+)>m z`XkYsi{8e1_?$3^jxd#)T+PGr3^SgS$lUk;p=DO_X_!V0ici(Kdu3(u8!k6SyrW(w zj?9Cny71QC)h->9fsQ+an-S=^#3#oH-co5|5;n`ig4wXM@+-l{s}II*EqXSX4c+pj zcdt`1(`W}&<#I3b_u#?I<$>*H9@ixsy;kxT-%6|MXdb891-Pif|OG~l&Sw$@$jxSCNZpEvOk=?LT3rgKl(M2rDlv z?OMd0tCJoKGv3=r$zrSfMamM5x%D2_N+BU3*5xn__d{DYpXXuSG(sThXkGxcH3*d? zuVbCs%=Xofnr_LjvICuU$l+znwObtfi!}|1LVwhLaOa;;nh%$Fz)_X&g-XBZIdt**X#$w`=2?1Gw4YM@CQ%~onl|N6n~wSG zuoHGW{fkNUyOKi-5vmdr$03)8hak#q%dmVJY|5EjLc6r&5>u4>AJ>O$!2wseav^^V zP|TeDk!F)gW|i6FVq@UBq7Pm&iPoAW(sS|)!o*QjBsfEN*xI|tuQ5<=NHAF1@7_R{ za{x4C%YnSMra4n<;Zy&n^Bn4ov6_O(HolFE?fbpVTVOLcJG;?jU_ErWGY})ARy5^b zgdpWE$cHR_v{IR!%6^GwPI#CU+6`X6i}s{{))u4S1Zs4Z*|VPH8kI8rtW?tP8D_Ce z*zoQ*!3FS7`<36X{Q2t#FHY|0^v{Ia#UCX{=HBrA1cxbcm93B@3^|5)(X<0wIEr{f zUdd{!JQ2EBJlo2?5{6MZ_>mrdQS1BGxaIJ8qW6HxauTX>Z(sU%jqQ z=ft#rBK42keCr=wH#qW|8?5LG=;A<}Ji3G7UJbZpYGGIBH{#a?g#`>eQkt0&gVPEH z!8ysaY2Lki%crxSzJ#V~A!5V`bxNcl3)@l_hh*no@+Q36+tX8)nUf7^=CmxmO@ox= zw>@ZG?Rne<_Ot4m#s?I<$>s%VQR0!ez*KGkfKepv7xVhs?ksF^|KuQYd!qmJG{eek zLR7h>b;fG_^}_ksV8@c4S72CJjmWzmq>Vj>1Fk`G_}kIN?{Fl*_y&$$iC||>@Eq34 zUi@>KOEg5B*|^7!=Y?9h*rbaFCq!aP(B*wo2^JNcl_XKD5w?aCHv!Bq%(c{edFYu~ z2c#yp{xf1#3;U+#J?_`^x(Ycxl;iMy3?EJO3gG``8lCQKfdmc~plCHO? z7=uk8wr04bQQN^G4T~X?3!j`)t!+GAT>Kg-GA{%SaKQjtI5Pct&>K^%j>y~mhQC=X z2Bb=V4jk}>r)Rc}Xax10_s!4%W;ms?67|LP=4j%SG^DoD>VUbX(AM3i2!7& z_^6^ca#(GVgPg9)%I^#u%Fgv7QV6D;P~wS#}Mc~sc}g8k)PYr2jYXf*))q@v#Y z)$L(Y0)C%r%QL$xG$hRybO7TeDKvZD6+Nj+@_ zJqgAKAO8)NJf3k}XR@=Ve;$V4V;djpG!sTPzZ7irg32J&WTt3DBwSl}U0zGD>3f2p z0{@paBlXB{0&EVE5{-e`(L{1u9~vZ91D8t)2*c{Xs7E#3VTfO|Q#8rRaR6Xh^*Do8 z9dJ%)thA$kqReV-P5h=3<6ur3ifgtZlU+yH3Q@2aBDL_@>1j&^KsaO0Cq$yrz%j|*?;ITF@CkL7&RyNLd}g2jQe&K3RY^RuM^ZubWa`yL0P+WUARShC)`rdPCZZA8Ch z5qlg%28S&eVi^2tcvjV`fw%w7v4j&l8nHvp|Mzw!$f#7iYBZ&@#L7v+5SCPrn(p8R znNNein`U?VHa#3Gc5S6C4U#t{y=eA9&ZdikEnHiX+EBH*`y%BV4_GacU{`CbJLc_X zuZ(c^UKMr(D!2bxVgiiQLAtO-5+hs8rt%~#B53nw_O$RZ*26|21E}jf{B0#3 z#T?yJYfq!WbpH~o%>{wADY-Ht#pGHTd7h51)0TWx@M&n8oYvx5MFM!l^MOWs%+Mqc z899dDk+=+#Lpln3s7q>VN&q>u;&)B|)EETg{ZwQvM(9_&KL4(7A!I}lYFH0-E!GUn zdT5e9yILoV#4UYxGPO34t5(a{TeW)kFN_y)&`S^;>k#D$``5N)8=c*jOYB>3R9>)+K!xZez;(fNsx%htD>pRrVKeozPmqEz)SLe#sy zzT>6N+`>&yMPypPsyq)LfIWN*ueaA2=fEg(+T@iIBSP}r9};}Q1MqVFkYIxO-msgRnZBd4XfNsY!N%nA z#&i*EwuMMj%2XN9#1!4=A6`E~S+$?fL#i6zyB_+@082GF(=~ljYQBWod5P>G89?<$ z*?p1rjYx_m6*qz}cY2L4_b`{@r0K0-7+&qh+$e#3+$R7#4_UKy)_zq7JIwHm{j`Cc z|4=JXM7G_55unTlgvhY>FJQpsdB!+pZZL@i_?Z9a3u zDb~3nf2;j$^XPnySQwrEpzL0wlYj;Sh8cC!ZMUusME}x;74A$as6L^efw9}wOg@NOpLU=ys-yN z-zX!|a~9W%Y2kM`HEG}LB6O}WOU6-2u)=q)al$G;wuW!Fo-E8f+TcRQ|5&G<@IcM{ z5!adUIKuR)(8ij}w}+qGNnHG1|9#|oIUHzD{6O0!% zCg^@_F-zc#fWaI>JoMN3#bE-+UIg&npMogG{!IMn$@Q-y+0WNfyFBNZsPP;rr~hhW6g>F*MzW0Rtz3rMoHuTmFj5 z7f*>Gd}wsq)b6tEzuQZ-GQTq3wi$1(6bMU!-M^r|AUPa^WLj*mt?`A=B;u&OttGpe zDoIgaxBE|7puM>F-QcZ&TW>bR!$%r^XAZ2n6m``RuH|*bm6>~FgJeViq}g5a>*BwE z(FcxWAIBX?LY6rQw-s3nfcCQ~s`+*?k&GnyN^fRe1OtcD@G9wJ&k=YEx`~ua<_l^TXvDR+gVbV<@GpIKn;_1%Cb-E+ z7S1E|5Faxidyr8t8ZOdjj|Zt>Qh_Gxh9ldi+NgLQTrW>S_lNG_ys@3M7Y%DJj$NnI zw#)>$lY}d>K=Gz#CO)Pk_P`nz6LhYZ`aQH;F}?*`LhPwmmomABY=`9ZMzJu_Yqx_` z0`DiN*H~)ZSuYw;wzJrGUJ?Pkq8@*yY>ccJqwipGSfT)#5Wv}%CM$q#Vo(}GG`uqb-D9A8H8q! z+J1o8D`5m#_L$|_z$Wj1!0_SJXNS#)Oc5c;$>!f2^2OgViX77Tlhnt67wb_GYS4nd z^RCmLr5&(x(v-Ls;{Z%`t_jd`Hi8p4IJju?Y~NDOI*B{ro=a^y5wz8JsU6CC9P3;t z4v}l9;;6(;R6}y|faNzHd5?Ke>llwFp3{w;m|a1BdbmQPTer{>gM;8sYK^=>Dtf9n ztJaTy)i0`^dPLG`hVGdUHNig4gnQ2#;*TLS4Uyxbpdb2(E|_{OLm~8GZOk*>qWdJrekuLnSBIVHsUUV4M>FliNt6;mCWH8o z=R!Rz*}EE@{>m3GHa3`19GB@;#5jG-)uK~~bLNU_+7YhSM1s**tvSm zbvY(PckhU}z!)EwPwg)w4?uFiYln1~liDhBcMo`Q-fQ>F$>BeAL@6IN{w?%u616v! zl}eQI5XI}~M6`vBdto1QAvYo!-A|1XwhUB=k=k9h5!M)`nG%yC{Pgbm$kFzUK#qKs<>Dj2 zAh%|7+(*Du+raij`9oLJ)12$zDof>noC)QwI^*q@EU~K|uJ6`|vhMV%4jou>l`A*! z&3|=CjRa2Jcod2U-u;Q0heX8&F_#oF=eN21*){Z3h;`mtFqngaIA?ulPb8-&i36W2 zDqyl`5`WH{eNdPOz0PTKV;k3oblYt_ejQI}=|srTW*Jwnb=RqV&AY@J=-@8%-f}gl zy`5xNzd#ZF$}N9%_p9Fg^LJZ*ydAr2ZmVm8h8px}Av?FbcXL;mWkB`juVH16v)Uo| z)9`mFmB31j|HfYGtO~kJY3`}Ztg7fZYq?fbzmsV zv=?8nzA%S4bZH*A<-ObAa*!V#)<8sv=cKpFdcN}gSpXR|FxC{yY*U#a}rfCCuPeUFMX_%u2 zWoXYrD$hJh1&UY(@E#+^Az}+Q-#gx;%H&8ivA$=t z8XQl8P}Q`A**o0^q1}d^3q)W*Rs?j*zf;kBnWmJR8o9AW`(mzOz?pI0>3x-pqmQ{A zMER=wmzVID_FfdWm2FYo#yRFo%g~0*G^YgX3hy!w;Hffp8#Y1J4vw`rQE%P}Q4rpm z)LfAp13avTr>h8vEz>aHIIA_<6(}IV^YUTOxwD8gRe^Q>#j6VRFpUrQKzQ)wWkORW z&UCOW@q>kV%sH=rg(IgNgo^h0#!OQcVOA*^D!x1yxXGp0rzILvW0UKcpPk~W$QdOs zG%1ichCY-43C>r>>~MW~ee&DlnW{Jy5|B1x<3~X(R8gtLdkOW|uF}Z|w*@gD`37>S zo6B$^>srORm*-$sWjk1p-tu-`|HhzO!Dq^S6}e6kFN}Ql>s9Ujl#m98Lo~gSi~`ZG zxX7nXcAT&aZmdqzee>}TUGYJUUZMKS$4X}{C=LdV4;&+5_6I`)y+W9D(c@zaJ5aa3 zZ%>%YId(LAy=h(~MBsX3VT+%#JwI>Fgzrqt@>;Sc$_bWi*M&)$IpFTP)nQf7dQ;b2 zMjV-@Dupiwm|YM0oq}M#8uC2oinP~y>WQRQh<*<+!$!#@^vCgMM(g?&7Ay}29i~rQ zxTi&DCD}J81~npxz-OA^rlqo)UhA`6dPh`eN;P(+Piq7E@rmP+Z49M8nl%MOhKQz= z0=kGZbyzH1Gd@>K&&GJ$L&A@%!=TS0lF>))2m3gW=1_YiBAA&^4LKXoPA7YF2j~89 zcuXUM z)9|N7!OYI#NsOEWb389#Bj(r0Cl70)AYgD5Hh3YqBI1?6mnk$%bmv!`M3+-WgfcHk z;bD^7+?v0-cBx;H>eZN}G?VT>kR<=c>*QEC2?`Y+2J%toO z1;OhK8u!c)3kwrZ88$Wji0?xCO{+l);66Ilo2Dj6Cba?lcWUFkH#;xeK??4DPOPvf zK?Jb3mr$zY>Z9E6zOCwm9Q+=ZR>cQ*X8iOGRHERzbiYz|mqp~IKK}x=^(QE2UL`57 zC*a7rz<7XiOjx*TEs4rYk+SQh70ZMTS-CtR43&V!d){}kedL3X)DQ8k9SP2F#?K0h z^HTO;q(48q)@t|`Ruse8y4F-(ZDAkYUzgBCnl(Cs8w-_klL8dGz-q2rZEPbRH=~6C z75EtI+cgViT9hUEoI`L^w-l~CXBH{vTX>_qz8~xCO>G=~+SK>6JKnvo36oq%Icg5x zjD>Q{oqc2-)%Ye_k$yA)TJ)7C-M9;rW2EO-Y*Kh*=B zfK^6i-xC&-zDgGly2Vkwt2e#o^zVH=kog!^9PM5BVL_qk3gyw&GSaNpaESOHLdZ(( delta 6469 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 CqqA%P delta 12934 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 diff --git a/tests/visual_tests/images/lines-3-800-800-2.0-cairo-reference.png b/tests/visual_tests/images/lines-3-800-800-2.0-cairo-reference.png index 53c62c7d5b67f5aa3c639c9df0896db1c979699a..d1eb386970c8236fb6549f147074fe7790e91601 100644 GIT binary patch delta 11259 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 delta 11261 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

C|`jYV3@ozoDA z$Vh9ejh0l=NIMv+Cok&??tEh#WG-y8>^FjN((FCvAKYxsyKUFrREKR6Kq_p6o5t#; zn!O2eH!j|}l+c+2m7$;AGRB9)r${0Roo~PQs*)xeuZPFC4;Vu@PYM=V&YuC4t_=vh zG^6B=+W|X2Le}Sd7Y>H4n1$wxGvey`jriGvWzMkO?~3|bJ#xWRcaS$1Jr6uiQ5 zA3FWYY%BAp^x~Z@KN2+Yd$<=es3rQIjI= zU+X_xQUua+4x$pt_hEWN45KRnNsgh_RKC`voL=f{UdUl#wRkI_>Kgi|MocF2qbm`M z@W;yItz&hs#+Bu019vFp6p3=I1&+Z!tf_IH$3e0}QU6=VeM%UI@~s+Yb0yk`dc+x6 z8zWSG?=bAw*W=gLpmnx0N(u`BQvrXLlNMU&eIhVR>!#^wv*VM8-Hnv^gKCZ7% zFku*oqjjXZ3kw7zy<+VY{dvyQTk3B4RS|j;f5_hSm!MbJBEFUlaDFzt*qpUnBtqK5 z-ripd6Y+ZNF-;%44RI#E{}pO~t=5d2-#^xuSl{|cs2OWQ%P1h?oEK z%k0__!@!CpxIMLpA~6rjl~l>m{s4O>!uyWs6u=B5_9N2TDnG)`vYm-j8I>^Rf$I6k zw`S`**o26Ty@(mJ{IH2|E8hNrbI%1dr7|W_fT%!%~E8 zKo3yP!1fNJ*;b{%)HfPCj1SLhoackNhEfTN+vHO>5rGz5G4nKP+5J%CH#e!Mu4;vf=!C7~*IF<+tNLd|F4~ z84X!o+^_39R_BNY@uzNpv-S>)?!`tT{CbtI2g=aa1Mlk7%_-K==NnLqNKb%av^K}_ zm*aixl@df}a5m|6TT#WnU)RN}X z&w5Lk)c%6S?_wv&!|=Jg!OuS+GK0gfCWz?aO4XO92TqBy(>tO&AWOZHo7b*?P;Phv z(M3JD5JmMr&0`hFlo^u9qWz&V$;jz5Bf~CF^&Q3;P!c<1=H+7m@1kq(gMMc72mPa| Mru(S!;omR+4~^tSegFUf diff --git a/tests/visual_tests/images/lines-shield-400-400-1.0-cairo-reference.png b/tests/visual_tests/images/lines-shield-400-400-1.0-cairo-reference.png index 59ee0a785d7aa6bdfd8d44c89ec03325c3c09730..d30efaca0934208c86ae43e1b757925ef6ca0d2f 100644 GIT binary patch literal 4387 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 U$MpVU{QO`tH-;NkKyOF<17<_(82|tP literal 4979 zcmd^Dc{tQ<_eUyYvdtib8Vn+2-$D(U!3>jZc_cy@j4YEalxFONED^FjgDlzirI=8* zEEx(BAxp?ke1GG4pZC4qzka{>`u+Q!>-t{TIp_Y|=f2N5_xIdO1lADA!pz4^M@Pq^ zhte^oqoa>I{+LeCG_|%JM>OF8i#EAVPe;edK%?Bz3 z1f%3BW=YT)VP;PG6Rg5#&I+I55o6($1A}ExK`{&r0s;bXPC+>+6wZA?noSVH%q+*t zE5?put-LuJ9)7KGM9OIfi7OGpdip8XbuPo)eAcb3j=sO-czh9K|CqY z#pTi^pzV_Lk(89TH{nQ5PF@~}kS`oGGzY_eIMFD*Y&54OXS=oo9m#;8awNQ}{$&aCXJ~5u+F(8m=e&E*8C9#)`#dPJ`%ZsJ|k2u zl5_uTJVNjB{mz$TsT1lg<0*dk#^^QYmo|>z2jBdx8@>F#_O0~vRW3vfg?(=Iu1ls=JUz@DAjHm= zn|Idyc4RdB{htbMC~G#9L3D+YXI)9?yoW8n$LBfR1IpF|aqwu}*n4iIVjQYtHv;_-0hj1zz8PvB64Z0gB#Nc|K)>QE8 z*}{&3q6J906(!=>m%x}~U)Ew_|6w+5h3$`2a?f#WWwWOjJjJMnOk(!n+tF$fbLAJ2 z*hbk)<&ML83bcl{-Wf8jp&??f>>|>4y3RR5BR4jO-t0S*5pT+<{Guoy))Iw#Gyg42 zQ^w!u)VY@|#l(TG0bUmvHBkCU+iM%PI$e1*;z%`bbDCX9FNrf1u?xN|O*4;Ccthr& zKKwWHSo8kSe~tC;lL!%9OSyBmzaZ*whmUg>rd;qQi~X`V;~8f9Cy4(CD+xAwV7Q70H# z^tnryX$bdS^hZQA4TB})VfI%>vWYw&icCWwcMY+K4`Ky%kh_zQ{}OsY`r+lEIX=&% zEaG!$BD+@zL@257i#KgSzHp5G{;798*cu9%%v6FabOv7$U!KxWiCd%E7J@eBM41$4 zUx5ORp&dlxA#XW)Vo^EIpUUgyN7#yWLA1Rl$ACTs&>R{i7@~adTA+MwCdU~IL!11O zFNcY;g`ndu`r#JV8iEHUiEBu^Hs59HAS>}~-+RoOHAmWoAQWw4Y!{W@=I6z^y2RF6 z@A!=zTrcy;seUxo|9Z7YjI+aN7LwTqZ!&#;zha6h9}Jz8LhTk@n6&;#v@?bp>SM(B z<2s3KdpEApTF@%D$)JoH1b4pY@3oS-rOxKB4_Dj|6-?jMUTYKHBev}t)f3(7iRYQ2 zp2$&rAkh=VrjHq2ECzj#y`VHb86ukKi+^OA0#7ytHJB<^2`4jNP_vCzxYBO)@@%A`jhQ%^8JEj;@PPltXR zO87PX1Fm}L7)y~~4f6qhIB9m$o~x;Kv3-8Sy~1)U zO;R@(Nfn)4KAe>4Pu5_{<;WR9nAuu)^b{01u|_3Dk&}j_8zSd8NPpWx@)w@_%W4Dh z0;MS-jb_wArJf1OlEwbqiOwDS_@6sjC@Jns$7d1U+t*L{CY^+`Olc6J7a&dfu^hKU zc80Iz`1u+Mz|)@W4ByC6)n~rNw0q}G_oTtwjy4IN82)N*5~7~Zd~{h^#ds~Hk$cru z*4P%2fP4s> SRae4UcXn=zeRqZ+kJX^)}eQrEpWpG z|2j8AQ3JZy>UbKb?)Y13z4|A29k=o3uTs)LL`bX7lEa^8jZW(Yuy*N9vmGLyO)i|f}okCt2HCzP+yYywBD{!P6y1~5>Yqw{pW}O z(e=3YIKT50idVfLtjHE z?W|Wp)Gp4oT(&Lo(Tu#vC(A4K7LOM1eZf;3S{N*Yxp5Vq#&5Qc#7}!l^)K9B@YheR6`VuVh*HoN0s4IxqYPjfB<1G@ zLy8&ITEMlsNvM!to_bq6KXdBEy@K!rZ?<~%W!<r0+)XX0gvO_qZS8vKpu10Yra{Zk<+^iMx3OKC;yv)I z^nFBP9oAfjzYnj_y`#CZP`la;_7D}JkQ&IcR$^uEqaTEJp9iJ3GMFPO6sLo@a#$Rd z)8hiM-Jg*{r9|S&ley*B@f-^*pH5@0FT`aKZn3})vPRmrygs6H8FAT{ zH{7FQxVE4Nwxu&KHiuL_x-UE{#BV4a%G*~H%D<1-Ts#m3 z*B_WJaCpL4NkpDL-Foa-k%UHMHT#Cd`WX3sZH4~SuTx35NC#O%N74k_jcD_gn+S9f zRPOQKCiFKcAgFRnPxNUX10gh7l?n8aQlxC^s?;ObE>_JOod zULRG;eqO2?RO#$wKpE6cK_oOfR4%JQT6;X=rIDHnVt{V80{Zp?5 z453^^+(a(lQ%)Nzee_V)aLn{u1CuPyId_O~Yt?k6A2qRTVZQxQjnlqVEH#UJe#C5b zI;wV9H=^h(`i-p&himbn4gTyO?tW9H!@br&8u+STOo({PPP8;E!5hCAxZ}3J&bVzU zPg;4=C)vT~o6Ym^52*EB6U%dPv8x&Q*x<9Ab6mOSSIT6}T9^j7kqGhuLuMb>Omva~ z|5J?{wgP6E$ES$QvD~6Jk%{)m&0K-O^_>?&DN1S(xfhZh^_>B`&_nk0CLP(%WEXOGW3nX6rAOL#hpnl;lWub9v<|%GsB@O$!>lt`B8RBD+uCl0+oQ z=GIU4TcNoowZLNbuYx0j9g!zHUR^ zq>03GVcxOO`Lw1!_{vZR))qkvKq9KZ19eg}{p!tI$098raUTLy9vfW;l?SXP^bOt= z>=ZX%1erJn5Z>Ov2 S2kkM9PVc&*PAS4B9 diff --git a/tests/visual_tests/images/lines-shield-400-400-2.0-cairo-reference.png b/tests/visual_tests/images/lines-shield-400-400-2.0-cairo-reference.png index 8189a805b9569f3ab9f31de1f0da842a0f74c65b..5f32ddaf07dac244b941b34aaa89d8f99fcc3a5e 100644 GIT binary patch literal 4915 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 literal 5019 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 diff --git a/tests/visual_tests/images/lines-shield-800-800-1.0-cairo-reference.png b/tests/visual_tests/images/lines-shield-800-800-1.0-cairo-reference.png index 87439c71f495a2f7a4b58442ade61a67941078f2..54fbbabd41558299bce14a206edcdc5cdc2b0b61 100644 GIT binary patch literal 8349 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 diff --git a/tests/visual_tests/images/lines-shield-800-800-2.0-cairo-reference.png b/tests/visual_tests/images/lines-shield-800-800-2.0-cairo-reference.png index fed3f6511e3a553e17a5f59d42e181b307cb3db0..4641d23ee983d3e71c253f497613ad08460849b9 100644 GIT binary patch literal 9346 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 Hu0mjf?y#t% delta 1347 zcmZwH`!^E`00;2o6_&NIc_iT!VuU=dc{fCIM|sWEQnY!kL*8RT7rUrk;ZT!n%`0Ob zlX)AiN#1NK??*Q_^0?mQ-uuHn=l;<5e17 z(!VeT0_on*sZ|J+B~}_PtSeYjkaAW1jLY$HtD~#+g0KZ#-$&6S;p8gBek6O`aIuF;io5W>)kkvjHK6OmP>q%;=Ct-91c9yFwi45 zfAvkw9oqO*#A(UhFXr?&6KiMCsRAnN(0C!IdGtl%ut~g9V*pP+@S8tk1GCK1S zR63aeOv|_Av{WB(2)x?inI#8Po$we@jHRZ>=Y-f7mu5+ofK$gsZlG}OO`ofj@tdEX*@*13l5E966%mJ;@Q4+G zruy3PF_vd>+gqD%`@W|gfw1+YkxWj`&_Fx~GU|d%0K^d%At`ZHs$%hD*M<;0JP-}x z`aD}~zq7iC16Gp7-yiLzG^3D5V+I1NHrS= z-(hw(w8i0Y1_+3^`TPg<5{`Hcyxl?^H44i!mCn6_cb62k^#Nyauu$D!!Gj?~=R1WN zw(v~?ch8^qd>bpL+XPZt0R_zJ91Ql9sR@@>)kH@c`7+o4GO_TL!(l57-~1kZgpt@T z54}|yn?iNz1^UaMwGo9-a_6>sw3A|FpWkA}+$)F=F;d}7&Hyev?o;Hy?b&m(lHpX6 zH58thsM7@W5 z>FZL0Mgm_4?&Yhpb__1akT_R!l-9=7MEdTt?q!bRXIh3tBvU+r15%bSp7{^^1xEo_V67%ESIr&6XKr8UxOz&1@rB#DXNV znzDrsvmk*LfH-697e5~;S?=c2AtC?v`W)9(Atc9RC$x5z)~_*KMl?BO=NWe*QF+bI zcv&vH(=CM=f4~OjDL4CM6g1;0b6)@jl*lSVnZJ#``krkCKQCCHwdS%sf zXpf{_ssZ8e8_k5ZOe!o)6tqlt*jjU#Fq)q=Be*A#Od*oP=PNN&dWYXm`~3qO3Ye@23!YN*^#?&a0=6aU2C#tB|wjZXL* DRP>g@ diff --git a/tests/visual_tests/images/list-300-100-2.0-cairo-reference.png b/tests/visual_tests/images/list-300-100-2.0-cairo-reference.png index 4cdace7a63ed4a755f72cc7ace047b19c96c178d..b0c77c6eedd42de3a9d90aab7c240980b105a120 100644 GIT binary patch delta 2516 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 diff --git a/tests/visual_tests/images/list-400-100-1.0-cairo-reference.png b/tests/visual_tests/images/list-400-100-1.0-cairo-reference.png index 4219f44117bfd3900743cab292d86ea6478a8797..ed891a4b5343473faaa5636bc524923cbbaf52b3 100644 GIT binary patch delta 1682 zcmV;D25tH55a|%GPytN@Nkl|hl3&Y6Mv>xfj^FZvF*(bZ~r=Zbq)=w>X&&{br2h$0$1n3 zV%z%cprLgB_RsNPNd3g0>0aRL@Y>PZPdYj}!v0vBJ{WNtVVA7$D(4)l50qZ0hkwq; zjxSk9J$Lv-8|QcGr8OuMR~Flz&vH=XfBaT$7^Ct?59r0aMhMz4cs z?s`C``R!BJ1Nig3@%ej%%VACq95_qJ?8U?U!}Z4F-D7F8#L8-Xyu6*P*WVqMK9`I^ z`8{rDv296!>hILMh5?gDdO)AXN>3jzzEZkz-Di2CY4kdH=B@{18sBbwbUlE-e}@n5 z#U~T;i^(}zbKp!Nmm3ccIrVy}WSw)ow8P{22R3|c3`*~D2a8{_1gJh{jrM@_fYLFx zXy^yNa{4S!G)+j8XH*=NX?&XraXo%eh?cnqKZqUK_+y z-l_6S|7p$~l-=VFmU$03&55lTf9(P30X2ktz4(6EZ+W6=_BMG&#X*_Ix0w*H19 z5OOn0>5@xhLDGi&wu(YPc+Tm zCa)(U9n4lCZ56(mG7EySaRMzP$7KmhLR^r!{r{f6Jy-l^B%X z<4)Gjl4=VvVe&{Xs1QOwEz^F>GnyvE=Jh0`gV`#it-_n((f1?wbb5Gr#=Njx?MP*N%Rfw6i)#dq(;~`zx=}QBcpdb=&1O;;ZvPx8*ei-_)T6g#Zf* z0TvVjEGPt6T!aj3IR3`Z|cB;LVyK@01FBM78C+3AcfRw ze+jG)2;9mU6jj7mNXkmOW9Yf;*7T1Yj($@}Sdu8sG?NVt1k=VzNB!pZ7+)b z=`1r7c5C`a4yP+wT@onGv{Kj|2&RqG6e4{nywmlP6-~N!2N5ENe=Czp4?H>M-^KhB zA+@b&WQCoOFMF|YY)j8iD2YT$j2O*oqDtviLSrjD!KRg0Bhf9lOKVqLc81I)@-`s5 z$2-#*_Wwy$S0kA~C0bonTcJ;yr} z>|5#ZL`EwHD$(i^*Zu-whi+yj2*w`coH9vXoyEr2d1X`C!Zs;V=iH!?6q20c+l6&X z%$3z~$y#BRjc>C;_u}l2abCXCc|n%I75OF01~`NeEpUlef0wu!?}w`FUl5Ev#yKn8 zX@xJrG`V8U*zQ|ivO;@Rmi-8g#S3~{nMN}krd3j7t0%Tksi7j_xeWbqLWm7;3JG4K z)nz;#%|%_^cE{LboD<6Ot|=}=;jQG#8wzxCp6`az0KInHpOzFXS8V z+8z&r<3gGMe}|B0Cry@Vbs0hm#AT&OV47o$qZ?5TB+9Jd;H)v$vLkbgwkZ4|y zvk!4Qf7^=?>lR@&tAwxWBoR-h*$|uU_5j!&CxkQsX+o5᫃X?L*$SyA|ksfPB0 z?5R0soC(^L5-%*IoI;#)14jPG#aiW=f9P^cpzE}5ID0^^NdJ=J&6KRVduisF9rst& zJ_7RB-v39~%ZI+(^Y1*}fzI|@uuK)l507*qoM6N<$g4SX=&;S4c delta 1676 zcmV;726Or85bO}JPytK_Nklp&Rd7z#2TCv1<3AS@r&p|_ zo;iM^jf*?=${Li3fVH!K*z#hYgBlmNYQq?nM|Oc;zB?_Q7oI46yc@7Q(KLD;JagB5 zGR>{eT@T>T_r~Y%F)oKWIdb4EA@i3Hiw}jZ$GgYUbcvPK_;__YU9Z18E`2T;gYtXa z&SJ}w0M*~AcMSt3kL&_{nkYSey!=Y(R$;*MMAPVX@XTHJ$uzcqZhUk-fWJqN?!_k) z@{7qi+i>7aAy-=u4;i(vQnJoDUfS_#{R8`lr^cZ49(S<#B};(n6V_-S$SzPi!6ps; zz-P{Y<%y;VY4VJU{W6WMnGn|lc)u|=dT$8%G0V=FSqIJ(a=x0$WD4|ZZ@OR*&sF}* zFa75kb5M4VJ6Ps_J>)zimSVIIWEZF*|a80`bu1!@R+*!uiu(DFpn>}~S; z64JqJ71CDWE4zuGm_q32E1mt}7_{yor}NvZR~M^$pKs}Z-U@$OQy0H%TUCic={@db z?X9S`5ECYk>;@G==%-aWXn97{gxI{kgmf@lg|t<8Gdli$?4Hh!j*gwtv$1jK70dBr zSZBYDdUK@Vd=_@}%cPyvIodbU6FOLVjgEr)uC3QDw-H~R54tU{A^4^aEhq$7PzbP~ z5MV(ez~Ul*WQ0X(Q(W&atcOB?t%pK@t%pK@t%o;tU_l|kf-HH>Av-LANw(-L zGZl1edPfeYD`{O4D9yBz-yaI5jnfn&eJHx8=OrtD9Cz*ZBSa2YCY3&Ta?HPr`6ohZ zJK@k8J0V{VBEiUxo}E%+v4j{gn$=j9((9PUW_XOPR$dQqDs&N>vmj;MDeh15bz zT0WtFlxZxg6}4Tx5)WuvUaVj?16fM<2`f}1FSM*IYq_cLiw|5mMbcWu2J9~I&KTP& z8JtRKMPDUaUEkd3zyrVZqs_ zq@1rtW;4DV%Co!q<}zzXmw5fbT|YQ~amlg)sSs}jdJFMnwsa-JF9!jaC&1nqdyMxj z^QlrLEke4!c*;jo$Tiui(r4}4OR}QV9LclOK3g$wjAk=S*ttQ2Vs7V1N`Kx|TwW-8 zXU?2Qvbnt&uBN?PI`;>EPP*nzAle8yA*hko|d z95c=YZAOXaml94P&UJl8{>#N$<(Yrza#Nt^YQ1pwfL@XQCZ#)5vg+QYnPYa`UsVSP z$Zvc9A7L*a`hMTv^YjV|@f%$fQ6`l@=Ql6|28H;JUcN5&@x8QUlOYH)lg|Mk7WohI WV)g)gYRDM?0000)^$2vA&i~>_NTW4KBJ-n7t=p zCX1}~8uqp~{np&y+;R@$YG8ye*P0jr`;1I(TooE@{UQOr*zuX6v^O3Lx>)VpMY$C< zIFxjzN|am+eY$PFwI0~e*}%55upo6A9WSnXsrw@mi$M)}TF1lPM;JJefQ>jQblKY1%=WR<^ zL{fBD;p@c0kH?D43Pw*akycpp^D>8Natf)))+5+y7p!3jdHL>`_gme3>hm4d|HhrG zYLc7^a{^b$PhmD@pZKbyMef2&Sza5bfv&XlyQ#GWSuf@HO7IGtx>EsC8#=_)D%DyV zp@5? zff+?vZ+mz!mZ(B{bG@Qt!Gu5U7T(Nx)r8r%ZP|LH=i}d~_HYPG2{fdZlBu{JCI11& zW9Z(@6x$iw#-ztBCv9+FotH3*@j-Da|2mtsl_#?d%oWNXXgM80cO zJi2qotZb;Hq(w}t%OF}HwhPJ3CT{NEoWTxyEq-lM9-qqzlTGV04nHGWfNWO|!;PJG ztZusqOwve8M+y2>n zc*cC3>a#hFhyNy|0fwb(O zhPg!{_)b}7{VJi38%w4fYM@V*g)B0gW*ZZLf`otSE_##<{LYDJtqDGv^{_di0RbtjcA0a{af=GL-nIP!X1c!#eV!W2>%J{3+99d-31_m z>aoU27oxg#HlR*58@@gL&A+y}`4Aq2{6nIilxfALdv^CMjh3x{-%Fe% zKH*G_rTh7`Pg3KZoIMF>Bd0YrP`%0+11!M93o|1Fu_EI70{)XQPMN&RF$_>F*Ea#_q zM2%bB2|8Z8ES18?6bXygFDNkQ77a%?v!vM!!*Sn(qG6U$D4>-Zl$5h~*0ca$_R_6$ ztpe!J$8#!S>kGGApc{b~c8!3xg^^aT5!L6=@%gXjo3fuBA)v~qp7)tKJ=zHhtBP@^ zS&@_SBzY;zv-izX=&RD0*C)hP8_3&5ALr3%mGz^|eN%a&bJzD3%8PQvp594fp^w@S zFNOE{pvQoK*VQ_KJ}qqN5`_ni2o{M`GG(H(A-ZJ5Gn;1aT(CcPlaq!P+#Fxbk+#Si z>J^|~6bHTs)%Va%Vysl$5}w}?vwXQ>=HBi7Ct07ibo;Zv%X)S8X}){?&1nk{oH)5^ z6N1Axhdq{PYMUwc4JQeS>G)cioo3I}L^*)ZQ78V0ZYo|L0J1aNsL%_m8=V)}Dy`mH z9mqI--Hle@IUZw@m8!ad+l@y!yem-#O25+qu&a8=7yW|IF8k*`=>LBy`hSojE#8SD S3qlMw%m2h_Ux82bwf_M*d0PYk delta 1771 zcmb7@dpOez7{_%vr}9vg%XUa4monrQnW!ZlA(wN^+SFPLiA@>{lV3@#GN)8itaNcp zyO?a$4Ck3p2U9b@GNkgLFpkTxWp?yD=Q-z(^Uvx1=lj>^^Stl#yx-e;MS9$=%YZ?D z-#x#k+?5xs!N?9k{B))K&0r21&A$qQ<^hJ?FO2kkS=Q4UAkZcsW@+*8hVntp+^&wZ z2#B@m4O4RoyhY3WC=qnjT40#Nb}tt#-|~F{d^hngV5`lH!aqrUIxovuJQhu4h@?3j z2`uz67RWTbNbdl8#luz=N-}qqJl}w8A=g66Yx)R6IS|p9a0~ z1=6&euB%Na&&3YJ)W5pB|79O2F>_(&ArSNUx zELraKkX~x1%S?U!<<=BC5Thz~bw>>yKPBbdpK3k#bQ^}3XYcfoBtnOAbkAgWxtbJr zx+WBz3)Q*vMFrI~3sTZr%jj!3dqfAhkPYZqfrPuQwj`{)2`I2B&(jzRyS$jlFxIhM zg%MHN;1oBD_$X6)h+Qghx-X8U7XQptz$_!3_O@bLy%a~86IgSwc$>t2yH3#l3|As5 zM6elvV-tjcVhIy@J*@G<_&)AUnqtloTmAkHEPUuZ0q_d5GATQJI=L)geOAI6iIf!pn_M z=d%2qj#dRvMI0!`fB_$F&jHlV3-YQ%rVT%?%Tla!QUJ=!TDWEF^^DAyEyd=>f`Pz= zn|oGn1tO?XUTuNu`R#j^v=BiHiMk{0&s^*oZ8vYK!=94k229nb_^~kxg5hXgR%-c) zPd22CIo%F$7(V@6A9t_APC9?;(48>ntKkZqI=}B{16^?5*?yy81)up2e{E;=eCmv4 z7RBt*T?7hHj>MCU?pB#ut=Jng*KuuNb<0SvMS^%WJLyAskGgD# z!Wz*D!0m#KNq?eVfzRUVW{b%2Gu}3F{7ZL|7%BbF(1+5aRSW3KM3(w`X>ewU9{{-* zjxFn`xcin=UKEWH}wkSMAm7Zr_GF%X&o7N1HHRbF$*uEH)_ULp0 zzqcD-FkOG$+rFw5%ntY*_?DxTjTpzOwyci2^+vgECP{$2Sz}(U;nr)j8`GbOC%SlF zJw%h=bQ`3q{fOs%z)3jF4Or6IDHVm&g!62+JI&&Zs9yDz`)Vdj?OY|9@z?vqwrE3M zSts8Zqt{c%<+v?gY9D_g>-&!<=H0s1*XwuqgOrL`xNbM$1i0S?euT^^CBc_8TzY7; zvGGKt4JfpKEI)#ZSDJin>K{zZ8UT{T*SD-Wp|*V3JMP2Zm{;HeqlV8k)0%A-t?a1D z#J(~1E7@j-X-q<64i0D7XY3m{*C;WbXX^)e92wT}CoPvf!|i*wxA7{8R#G8q4@&h& zbkIar6Xn;P09f7S+nv}eA%4)Nkr33P(F?KY8-MDldNY1Z-_Xx%cs7_?rC74Jr*$OLO?|YJF!Q{^{iZ05ev=4D=2}^D@3@ PEsg)agT5TZ(agU9F&I-w diff --git a/tests/visual_tests/images/list-800-100-2.0-cairo-reference.png b/tests/visual_tests/images/list-800-100-2.0-cairo-reference.png index 8a716f1d5d628435bc51df6069ba843b6525803e..1fa3a7bdd9a7e872382995caac3dcbd91970424b 100644 GIT binary patch delta 3872 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 delta 3902 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 MNe4wvM6N<$f?L^CaR2}S diff --git a/tests/visual_tests/images/marker-multi-policy-600-400-2.0-cairo-reference.png b/tests/visual_tests/images/marker-multi-policy-600-400-2.0-cairo-reference.png index 5b157353f85246f31ff4d6f8104f887d7bd47655..1b2a9ce26f993963ba736963d28a274a8b61060f 100644 GIT binary patch literal 7187 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 Sx!0@z{4q7MGDHl{NBkcNwTD~) literal 7236 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 Okb&-%3#iV`fd2<^lciDs diff --git a/tests/visual_tests/images/marker-on-hex-grid-400-600-1.0-cairo-reference.png b/tests/visual_tests/images/marker-on-hex-grid-400-600-1.0-cairo-reference.png index 0a730a5db89570b3386db21c140eb1095088b16b..75362bc955c5823536d5442cf98351b66f0cafef 100644 GIT binary patch literal 23241 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 literal 22652 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 BYx4jA diff --git a/tests/visual_tests/images/marker-on-hex-grid-600-400-1.0-cairo-reference.png b/tests/visual_tests/images/marker-on-hex-grid-600-400-1.0-cairo-reference.png index 28f7da0786c3ab2a6b14746c5396273a16bd7f54..de0885e9cb65cccddbd7e7584cfefc72ca10a134 100644 GIT binary patch literal 18468 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 Hu0mjf^DfB1 literal 18139 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 diff --git a/tests/visual_tests/images/marker-on-line-600-400-1.0-cairo-reference.png b/tests/visual_tests/images/marker-on-line-600-400-1.0-cairo-reference.png index 7bea4f62fef9c6c07a112b596a373a478a92066e..788a1aa364b9b6186a4dd913ca9bd3220a0ab98a 100644 GIT binary patch literal 2442 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 diff --git a/tests/visual_tests/images/marker-on-line-600-400-2.0-cairo-reference.png b/tests/visual_tests/images/marker-on-line-600-400-2.0-cairo-reference.png index 22b1822ee972589cfa42668cfd0b748f1c9a0781..efafa2799f2773006bae84c9c34d88a3366b6083 100644 GIT binary patch literal 2862 zcmaJ@Ygkif5S$<6BeZP0Unas?Whn(e6 zOB@6u0YMNBVM~Ko5`>kUAb8qw)?h;V`pfX4T)u2o2tNPs-&lee|Gxw_1cA#Z5ri{A zNC_hCznWZvC?tr}1W`p0Z3OXzAl@boKODl@yI-t~`iS696wUSxRV1YE)HQdQZukEB zdU$AP;AOB|2GR%;!-4}>efj;<9=nxOeJ6&r`zY(Ke|PzYGIRIWqtDV_%^!W1FLpCW z+^MtT4?H*~E=l;a>h{{Xs%Qq>$4Kev^ETbP%dFu05uX@KX za=JZiW%6F+H07nhiONgeqzr^R9&XBXxCbwcvD33mG~=?%K8^=h7X$aFkFDXBsNwGJ zCg2uF!d=#F1^3uzoVJ_iM795({xx4Qul6wRiQMo{xi;jT9&6CW!yH}Oz|qMIIogkK zqE@7TYse;>`&~dBbhfe6yPuy4MWWiCWKZb!X7d#X+Ea10j|f+F22TzZzV(QD4GCqkS)&kc=2)8?#AWT&%;?LJr2c`th}5}ieh|_ zb!PZjJAHES6lFu6dN6-Z}&gic6QFC+tYJq9N?Rts{`IX8+cUvV@+&) zO>*5b=I(k+$LP}uBkS@q70Sb{(~ksft-nfkyW8pZ9~wE+Iql&ZyQzB)v{?1M=}&1G zdGE;hlgt>;}UXuhfDUR=`;zx@>@G5b6I^~;;cS1(??JTrQKX-$2GeBs-T z4t)dNZ#ERiO_MiuHkp_sm9-5w^$VDtLkr{TGB2JBc<&KyYx3^7RR7kuCSWQDHm9E- zUB%XA{KtK=F5$aPtM$`1{k)+qw9%X`wZFGgx`>XtPGT z;Z61PHsLeTpC>((4o$|_t=AV)ckZp;eECG;enV#elQ$3hPxbgte{y5}O+)6>Cr1k} z4PG8!F;A!~cy&!nPsysb*C!uzMH^d4boApSY*YKz3ut`9dV9Dk6XzyjvD%kgVGzg1 zK^lUIi?I^wHB%Uu@(}@Mb2wH&vXAT6Dg$L*1=zA5DzOlqscx6Eb$cbaQyr&(v`3(U zR3?J7>H(yM1sL5AIx9#Mq=szW_;M>qW1S(b6MrqWe;Xu=?!=n-m5U&Wu|QH;hViRc zVR6e(mSJJ;teTA5PB-JW*_~KFzfwO)Lo7(_^(ppKz1$8`O;HwKabJa+cbuIh*9z-p z&_6Fi;m1{DK~|yz!izM>UyE>Qg31nL#axh8Xn^cS5$bkTVJUa~!~^L)0i+?GyK@k0 zy`#kzBsm8p(+l$WAeX3_OXMC6@>o0)B@FUG8mEAqh1V+6_JVXBE?#H7d{cC(->T?Vy=biDA+1^>LCZ>NtxgPzq!y-Tub-{V=DKS+V_vu+4`u0VLE6a`1^ z$GqcDqmQ*D0Sz+_$4xcKt96&H6D6JFj(xaTg696#2r_aP%9MI~gJf8c zO+l!uHP->8F@?m}KnE(yhM5x?YA3H{-A)gmXxLbfNi#C_Kr~~c&2kM5{bc2L?w=q zU)Sh%6RKcs!BpiQTYbK@ZbhJMZI;xF4Yy}PZ4_ZV(=RR(4}-{=I`xIEeBB{Wuej+6 zOU?vz^$&e(p0}@N=S{o9y=hJ1#`~UqFVY5viY>0`R~#fB88zhDIxAgOeV$nzn^jOW zN8j5oU{-z}Lic>alV$E@2kRt;@U{tx?HPQz?-tjVfl5L$dp6~JSEw|bv&l9D*DuhT zw;(uft3LjjfJswD(R~QszQ;J&ATczb^;BF!@JFA9(Lh-;XmbUM%ltt5qN}}ahH)Z=oz>sCj=tjWeGRgyR zLs=Mo1+XNO3Io)Yh0@mmb>UPB;FGe&Gz-Xxs2nb~d=cFYSoMG^)0(+7L-g>gyYN35V}UB#Ad~ucBI0d3K&6RG<^%I zRS&5Yz}&=TvH5WVFW?vNQ;)dQ^^54c zfM4A-I`#^cG09>@HWG>=XGNqU;i)jn1EYC7ZwF~acTWu(^P%b#A0k@;t9^_e9F*)M z+X1hpv5t2DNe$Tv*qLu!$wAKo@;0EkgU#olR}Ogx(BjB;aB$NCMI#d0>~V|uGB&I8 zL>}dVaTd+XCCyMBx~6r*I6u9WsxQX8D*VFfVN|PF!ggR6s{6bZ4-mZ5c}4`L+o`vv zO1a<*g`@?+J;Ql!KO(qyqyBd;xF?MM3&C4HWIM0})te6Kh&n5B>}UBxWo(xDxPyKM zCJ6*6UP6F)1XKhG)6zyL@^}d82E43X@o7Q?UjOf(6AJtfFo7UYa}q&# z6GRX}Wd8S2H9;IDh<<{2NDyzY6jgr(JQ~_7OAehH@$=^`4X0b3WzDa~Cn~MU;rYwUbX1dW!9k%z)pQp1 zVN6j@$C}TchdFRIom?|ld9d)`*&S;n!jhj>89R6Eme7xtZ!)y`QV-AP?14b$Wr7E# z$^JsYEZwo(Jc4CSE6bI(&^)<1qIh#5hor7~V40T3hHF^Puq_T?V?r6W*$K;t?TovX zkXVj9aWCMI3AdysVM$b|vj??u?J~1U;%|3Gi3o3wD}B7K7BGJ+8}RE`!25)D9nR+ywa-)X=Sf zs@|Ii?>t-{KB*|L6Aalx;zarn#U(^NUmSjrUiD-8>wV ztB9FDar?Pfr|2*|avIp$u_JfK-O`Bt^SZQ+vOk-+xg)yZR8>$+?@;u>mEoRe8)mK! zkY5?yJT{su@64T65}aBT6tgJo4*mG-+Dk{jG9A?|cbzV4zqWWk*Q`BSvftXd$f9ij zvzt>A%r6|fZ0#E!Z#gIW?E2w`Z_G;jaObu2Pj-0^8m1Z>UX;F?B6h`q!?Tia5@x*c$Kk3?a*SwPqE%qk-* zgAFuk3eYnGrmEE$s4)$wJcy}kcn@eCkExo@REQyuHZYFfiMCf3`*>HpwCY+=Y8klfmCxV^{ zhwBh@OsM%Dp@IpTUl8^(VUHtx(hn;Xw47C^Bn>^&b}mNoBhT`y31HZn~|K+#~EC;U~a!yGU|{$e2Qr z6Xg4az3-a`x%5M}u|e1?F~6EhksSF!m86!)m}Q||OgSn^Igv5fLOYwDsU$^&eX%;x zN8#;Ef8$^*lFBv~WWR6mcQchblBui#jj(>UIYdBJILen58C8TmTwO6;p>w8b2V=QZ zwxu9@o~6glbkUK_WewB_#bR^FG|J{EUteVW2a)lyy23}%=S-J57)hxty&yvYhFlrn%%rBy+}Q z_P%T+>g!6W+&g>)-?~>af@E5$vE?TfB+94Lw~!oVUsCxN$$$5@Jn0mh{hK}_+2`1j zqU3UsNo=N%e?W2}%e7^CMxGFnlObydHKLP{22WA|c}k$sGqRpX&Vc+bSuLU=jV#h1 z@`|Bc&&Y;gavtQp;WE)F$VPuM1oEkWnVykNKI9T~$FeT$3STmqd^5 zbm?kQi`Z<~_YoNfD!Hay&&bLT$Ye<6jWSUyWVMJ~2dVWe(=$@*Nv1=#ks8ry$OAkw z6LPRdt!L!7EK&*is-j$UMr=-B;YX?aq&CWMh?kh0WrJtJkl zq!u#ArV+J4%H7E$kd>)wJtLFnkp{?CeVGV*nZ8;;(vSlpjh>Nf*kl{zv%Xo;+mZ^b zhtE5+6f3R`Zp9snTY%thp-7S9?(VKdZ{BaM zZ>{_H{+;LKtju%H%ws5&%v@l15G*M^7Kf&hCVY+Coho!Np~dg;h;Rn9EEV z3;^`u;+CmVd7Vqx*a#B3%Z$)%=_V`uk7 zLK;Lxg)=chn3;ocacj7_oj-iAlq7XWMeU`a$awqK4GHNlAt986q==C*0t*ZA_v&eA zk~ld0kdVePF}t||g)&Gnnz$~gsDn5-E%fy9A3oTFh;581j0gzoxVgQMkfw2P+A%Tv zI5~sR&_=kq{rLD?g}A;WAF6#zJr29)ygh4J%4L`A&;fJNv;p>|g5ReRkq(??(MMvi) zCKe_om8GQ>WoDLVWmW%;j?2rdEGVcgE^aO>tAj#YV6ev8+P0zUy4KeA{i33lmY$xT z-iyl0-@k{3hDL^mCtljxCMKq)r{|vs2bY$XR#w(urlz*Gwsv>-4h{}ZPEKBqk6&J1 zIE_Mq0Kj`iMHxvgkIdt&2NNRQ=a{(iFP{|)-!AVDIz`KPPYM5x z_g0qiA3enWdgk1En%ElyFpvfEI|2A<0kD8y0620GG6psV79AHI+yBXs${`h7Uhlhw zDHsR~C>?)!`s@3>m~ne97B16>u&`^?hNn1jw+nXr(s6yk@(@_`Oxm)15|spX5f5y8 zWRm$!x9rEt$I?6@!I4?i9|*F|``F$;-{rKn#4+ zPZKSqx_@i&V7YvdBJRhB)t6`g`D2kPBrwvecJ{5zM4`%_^xqTD> z*27a55x*32sm^^+Mr`QKp-s*G%Pna<|CXMP>rj&VPPno}y8XXLgj4GpgV!e|6HoZ~ z$=lY8>Z+olqDj?(x~aajw9#UVS%DIJnfbKwsPG8TM^zL5r`}!zl@cp1Gq$V*c^Zkw zG4OU6c(k&I;;h1h7PqA3+w-Kk9&HaT(VX%7#8o5+NgoFFwu#S$q9>nNX`SUHeDG_6 zoR7f0`%y>iYmKIAZR+`7hX7wfI_O#9Gm7G;zyFd7=#8W}<*(T8ZhcWAQ@e%m&OQ7m zSj^XUWl7$R|4BfDkW_j&PxD7EDiqumYQoPed`}-Me|yrHLXxCAy?<5Gb^iJ_E^U&7 zOb30}YsR>QhK)9z8`ldd`YBegW5k`IuFSH?avocDd>JM{Z~6`u*)5k@!0N?#L>hmH>Zx=OYB@zd|C zT%4`WKf*on-q3MfUR){~Qd0R@>q^^V%>&7%*GH}-FM%|7swnu$Hv6lR5j`43G1Kws zQklA;@+M5QM$~O-ig@_RnD`$VDMV{Gue=&cbje%)oIE6c6N~e%J&(KlLas>!fb*uK zYlmx`JH3}x)e^ECCCrwq>5`>+d`@!^S#~L>myCJvCzK@O()N`E^&C{R(5~bI6$rbb0nXm#^UK@A}h`*12(Iz#hdg-n(Xlxy6z+(6Wk}Nv;Kp%7G=T`DIc8s{DIHt5m)wRHC5AU%E z34^og^%!$uHaW0Qq}HS8X2bYMk7I&Q{0iM~5&$8g>wEArq$pBjY0n0;@)-GB-T2-` zZIcLWcE2BAK3Y_LjloIhnsll-R8zJ3**UcB#8?hHQi91?{-GoEe3&jyEBnA z`$;QPa%2onfQK`Qi?s$jSjkT7c(|$j%IVAHO9Sxv@;Sooaw7TTi{aN+_P1A<0I+J# zb6P6OvQ5ur{2%@5f$V!j99~N?tWu~SnxUAl>u>Kj0C>6p4M!O_CQ-kq6fj;aSeVac zpE2vKCZ7cv!tbz$A|YQY@;%$i?qs&CC%bSfVOHvS`v)3^-2_JEOi0MXu>Q6w?xQ99 z_nS?gdEXo&$I|IeV&Hb1()}{{HHKaMYvaYH4Y*LoS?6-}f4n zme8{R|CR`^g$T)_R}g;TbW8R@kIL*f^Fs>7cfJ;@f-OnpVlwT`AM;S2U%vx3O@GfZy^h!*&?8oR#nww@ZqkR-RLCZ=uYFH@Yx%6tL|-P%=} zlpcTu{Omtv6Ov!wH8#!}+r7W*83C=ZTOAg!QkMq6h#@U9PI{Zhp4EwU`C|5y<^Ot= zs`#VhADDk{{t(uyf%2-U6w4WVAi*#A5=)xUn#deC8IQl~3-{(c7hLHhGB>UWUDf76 zwU~FFW_h#%6+ae!@^F<*amTU;H8{_+)n2`{V_1Vlj$S9fHwyo2=)fc@gn8XFnYbSp zs&CI+$}ZC^vg8x*^MTPB_rEKsoR`S`SgVv}S~l-X3zmzE%Py{hIX2Si5Y2ph!dvxw zU`%jd8GW@i|AfvJOY3*bUVFL;OE9nc3?X?XX5FE}B@Ii4iD2(U@)6zY!$-G1;fR6h zV}sgGDZli5n*FIU&?q<&25PTarMeDt8>8t+x!r#?Vx(Z() zNwaxZ`sGtgBpXw2hviag^EfMZWc)c8R6tMFWm4Zs*0Gv8vk7Ayj~_JWZ?E}OAB7fZ zw{19w{`?trK-E)?7$BUXhQ~PXm`V!@|9AJ`9Qk~uXQdSAeqCGPY~bkLpBn=`6!`qk zkRp7m>Gimzt$c#Sb*t9|%uuWaO(h-GJ5qE5mzUQ!M_I%^TVeA`ULn+Ab}n7Tzh&C} zb|=7;P_oMp&MEjg(a%Gx^Tu|%=f$ngT6$j{&XFe>UkAI{?Jw?ijvlY7$o=K7R`eMU z@?S01aP~JrKfm+XKV+MlY|iWAMf%;%cm!eA`Z67)yB0j2w(+*3{`9nyt$k)L{$bpe zWfdfEBdMV${kzbVTyhSRGbJ@SW=*53|3CaQSUK}Zm8rTv=*M@px*`I}fKim-{`4r= zC6@wXu(GsSmWi2zlCEBhs&H{tygNr%85Eh3+Tm(FWxSxkItN;RWudx5B}%|5f4LYY zA0OYJ-H$e5R%c9FlDFbBsd?xyXOO6qOxg|&j??Doc1_g;Nh|Uw8~C4WOYsuLR&;fn zXnNVZPh6L$VNv}+J)hs$R<&|Oq-hWlAJ!o*>G%HLHSTXU$f4%y?Nvt)pjc#G*-r1a z{G0y!44pqNg7`#OL$YO7^p@Cej;x6K!_Jk{o*i>#h;^8yk*W^T!joW_s68D;91iAO zt=4qk5AK`mPuA}=LGVN(vfDcq5ThX8NVbII zO`5*#ERW21NW6Vuq`|7X0zOlii=eNQ&jDo7?|h z`kiT>4m4KODraazcsIFF8$9jBvhhw1Wah!5V@&8Lq$_!VHV=H!kkgwVu|ysVUcA1> z$->{W;p)S%;{%(!c>MbvS%APj+WEMXaoIOsJKF)|?_@wY#q4fhPKum0ff!uscBvQI z)Ob&g=q%FTg82_l4$UIxaNd&??vx+S>mGIBoo=rk>j&U`VoA2a%!5I0A`S2g3LV4@ zl$+TPQw=#!LOiImMjmG+@h%Kb*4112YM)T5k^-46bb?r+&)UxzWG8yOS_!3H-IxBr z8(%YfqRynL49d&R+xgqV3hp2X`X%89a6KX~+f{-Y;mA?~h*}fb@+CfNPR7XC!uVw0 z`uHg5k&BOr3*0<#vEyj=^hS=1C|&A?c;-t{PGvQWfS5Ssl_ChPsYHFLEox(rI(qbw z3|<+YdHS7z{?)FH_MPY?N%GkA{6e=Strp)+^8wjeThGbE###F&j!%ODjfPk?wC4&k z4Qvo`X!SMm@k8P7+R+~&+ht;BQP1D<6F+yI<#>^F{mx}>8q7LLko-BffORe*jJWfe zKgu-;2s!xS|WeH-OCZKwFC|jfIc9 zn{(MXX1=~(70$3tis+%61wy1>&UyF?ipFvfNG^jmK2|EkWb6D2ZaY|1M9npEi8ea3 z)NhNXvAif;esxw$n1G*LzAmRdgyB2g<1vsF|FF_8;X8Pv&)GvmdQrW1bu==wA8tXN z%>-Syt3J%4%6T%>q!b<-&fX92B`NgHk^A#xfAO!V;-a9pSF-n)!WJgHg{Y8A|Ih`~ zAic6{-QZ#6UUk2|&x=^+^W?*wWNT1qsO40Se3!+4r1I#XfOj;ch8n7X82TH zLa6N0vBjx`O2BtK z)wKVsKTn=+f|81+0g{@>uEEHx&YF8;7r0yhU%Z%E`th7w<}&DYSO8hHUe{2GgK>(@ z_$HC+CH4k<5EYb69B~IxGWIl{39nDO?`y|3_$-Z2E|HUacBonHn0-pN3LDp%128H1YA`Zhcu0k&f>aYZ@_!B80HgK!BL&Vi8a_u0j-ZO zAM(-mS3VfV<*t%oFxoGA?%2y!k7cOE!-HN+C{F^P|8$Dq6|{J3&eR=8k4n5`){JZ8 zc@a33)IO?>b?|-hT38X~_(M0+ceOavCAZVcIF!nqtcs|$R8(={PMDNb5>Op1EP3^AaDtr@dgXjJ0?M+fgK7TZud6!odG2*g z?^d<2peWV!%vjhHeWyxquhsW@l=dVq2MPN3Oe%YW+&(XtKN#lOZyL^;NkacL_(|w< z&u;~X6b3>5|LpB3)pKU@mEGx9vUOf}rPf-DY-6*;Qm?Nld6wU<9)|^0f@U@)k5@|O zohXMEuYPZbGs-gGi>*#07GbHLRD$^ZR~||JMZ+#@sWw?Pw@5g>Gh%+&u8|;$Zc4`) zCTB*x%KIalKnf~S^KZC2uST14J!8EI5cKbnNDVwZ`-F|b%*YoedtzQYquOIIPqB~9 zB5H<|%#>2|$9*aPE55_kD!-T6wq1uBD#XG@KSqu2joYej_g;jz=8!mkav@mxMEQ80 z-2~qLbmcIVlPHV=kFb_o^7SPG&D%T3W#} z#H3rrGJawcNT{sk4wkcujYs1mS^|F2=kiQK@vc-_(Fm4WcPd)$Y32BVh0|&x0l0`D z?Mgr_mf%FW5pR8}k3o!f8dD6kziF$cz#tMD}VayQ>*G(ys-; zhGqSImt(JqUX52*XZAX-Ob?uzwL}zz3L$=A!O(Yz8(tXFoTBMJmC*ej5vr>x)1_`^ zFFI1XL##kAwx5U%dcNe<_z9aF&2;R($z{K?q5=Lwaq?zvpkNsjCX__*Y6?S{ZJewI z9V^gFB9PBw=#ctP6618UXIid0IVw0U7<}<9ICd6Rhv*DNU8`4|j(c0EYkRp~MY&DO z)xAanr=g0N1gf)!Z2OvsO~ys5r|wk{kv(8A&~YV<^_qj)k9o909tcBH_~5)&>~{Fc zieH{Ee8P2}N|Em?6CdP!w=of%IwY>8CNF3x>t5I|)ViD4#ADYz`X0zgIk0$vOu9!1 z`UJ{dGw!xGr*ts!HR|6Xk7oQ3eB*1!f9qWN+EDkeFw5ISbT}501Z%le>c8@xx6ccf zO+56T3YRlF!;)@Zyabm)pU*Vb${0i#ns~6}H6Esr5`YZ%^4j~9k~XaPYgDfDffTV~^sr-MQvcs^qr``3)NAk1 zSNJo)Nj10Oovc}yhQ(^X4wiHJtO_A z(mSSjX2;7iJ(%KpAhZd9x$adhweoS?+qeIfgv~ChOmh)AT=CR5qK2Vm!{>qmpU6WQN_DE7`2KnNXS$T(l%J77 zS&hHzmVSXJEr#+B0S1MHoZQl2N~ph*APu!JZV(8#|*JoyX~4 zv#l`M$;Rbg?mG1a8=ihNjgvRU_Yee3L#D4XUm(oVJV6H^bJ=ZHi>ax!-Cj*jhsx^A zpQ&QmVJfrr@LFbuFEreEB9L;HNaQgs+jqYZ61C*wfO?3w3=M`|iuz;3wYrm6*}2w< z@BDsUv6unrO5pFneoeUTqEx!NA-x;Q!tGP2V);6mD!%*e+Dyd^E(p~I%EZ->c+HKV zS-zM(_CylLbZ%dep-fQHnb>cNZEF23?{Yn7FkCUY;+UY-9W=VDuQr*ZD(|KcrKiHp z>a%-AP}0{ynHZSiw)aHM(Su;(-W27H7Z9`lm+vYiioT%0&uP+lw^fWiLU#CtImbWm z%RyI`)K~vyq+6?F@=9bpt5`>zjoTOygAAy>dQRuB&18&c#5(zlR=Qg6`c2mJ?L1MB zIMEP3gET1r{qKcdo8)HiNA5+tW9^Z`vC!h1qnSG9(NPh{9Zg9~7G8)fddrp(OaFi1 zvhY{CIQ%hZ7Vy};Zld2}w^dGW6{UV5x-%A#xN!t-?*>!Ty&^|6ow*3Q6!{1zWjuN zB;dlHkTYhOC%;q6@=pK|90fxomq70HP{^OvA7we<-nD4gMc!dxgA?j^{1bWcsqhp2 zkx^IPb_Xw(u({SR7gk+CC-+;I!iQ`(-|Mn(-_@tNyMAS7{lI+uQ1|167ZZ~kF!a$7 zqYdU|RiZ!U@;NCXT}sP#Mc7Y~|FT)8J(Hr#^_3y0aT6mWta6t@N-Mq2yis+yeTttW8N*f4c2-4$a@{Npbp;xer21e~)X;4^S@EcIO+F#YesHiGz+=J<$oNzG05NOE8y=PFPX z-4BkG29nlT)hgbtg5UM_@%|h`{tcGRwQcec+RcWhi_r$ZAX+FR)10riL_|e1b~6vn zsw7M4hD`JM9`8_&r``_8y|9|Ks&C2TiyRzM(m75I&KcTScSC{l)4nEeDI30x{Hl5l zK@kvebHtALlIw$sm&)ST>ednrm;pnF+*%D5l`YKDr7s?J{|h{rH1UE*odSJJbWuf$jWs1<=XkF8urMYkTW|1<-|5=#iFmI9Y zQ0-61+Z$h-^X;|UI#Q#GXdE#}oZk--q8;8+d3$URwDu{DIAh&W-_#1O-XF9QLffv+ zHk{a6m;Kc8k(QC^IG6qT^0Cij-vo~iKdddHSQctek73t8N4h}`fY7kee&| z_9C4mfL$Q1{A~WbpPl~+qcJPmeRRsbXsk%y*Wimrz)p8oN#?w;$raf!(2^y|^G9Cv zGxwsSgYf>Jc&UU|PMwh)L4R34MpuVMHly(-h4}3;_E~X#-QFBO)`dOUsI|SnxJtae zFz;7bn0m=^)|$+mOexy@h@ySWNrrA1#S>UuQ$4(lpl)c=f|ZF$%dVR#sWq1S1Ai~D zu^WrI^U$+%-L6GQ-oA#{P#phbFA>za5a8Ep3Jt$WY}?|8Mss5yuIOJp=ewGr<0tnv zwa60m*fskl+*8!T!eg%b@%---kA)fCb?k(KO^AdX!Jh`_`dqZgU^!N72EuEXNvUZ!8}UsiKChAlJ5?Zw@cG z?ecjNlRwhOHMKZ@eqmJn1R_vm92Yn)fkH{q)ir(ACr^KaQh4RQDP?r#s<`oQOZ)BG_Lc#t-1sxfM(jti>fb zqCfNbs6G7HPZU^>*j=SIv!o>gZcix>*;Gam-SBM9C2S1NHi9yunmEEEgl_eGUNK*% zR-^H>MiIRN(u{R*C-(I3tD9Vs1c68PoTlvul`jlt?1_^`%`fKnB&yr1h30+77d^%g+e|nMzv8DX&#)908bxJYRBapd4mkL$jrQ>4n&uU0`B(^%ZdWx6qpvvLvyfqem zp~#ZBiWrQRCuGN_{PAXy7Mb`66OM(qpz9D<0P{Vq8kF_=b16DO1`}v8oUQFsTl!0a zdW`)81`nbbUiVz{{Z7-dlE;#Q>9!^q8BKAVsOcG zLs?OtW?1*HSJM?;l~;NI?6vfp{QC+_SM>q60m^roD%J=$hQ=|xS;FQuuf0>klV_g! za6+^;0buf;Z_Bw&^`bYK4mBW#dW3jk5feUpe_m7(;-M8MWH{bJ$yjWf>Kr z-|8+geWR4l>&Af=Ax{jX_{m7a^h+wv&N+<{Q@HvM&y2WJDr5a6p2f}%6*@mKdH`V5 z3<7r(S{w1%IM!~T24Z_%wf_!cAWpm~s(8*--E;o4-T_7NWQU_Psz$`(ai!P(#e|gTe<}nHWXd_Kzsul6~mUBGmhzSY6`<2;E zhC?(wAiK8DhYMR=_M#?R^@>mW%`%4(oEod&Tg?mS`mH4ut@ivTT@l_xDlf!r^lq*x zl}<`#_x`@F>4j)gvr+oKuUVlzDmYnHeC2@!p6e}M6UZe)tE;9db$tFG6*}vW0w-U32XdPwY2zfjBxyEYpNNsnB4|^Ips?w-V z%`$g+RXe-FB3jM5jbF=sVlw)7Xw>a>vwYh+g5YCKjRHxl*m?n`nXe`WZ|;<5l5c?r|y_Mx?hQ!0z$XR1i9TM2)osPfU$Mntk+p;8~c9nm! z?4jx^ZoeE{*{1_-jfO6Tw~C`;qk{kabwtc0aJBO#HnS*~#_y`_*yub&<^Mm*1Br@T zt<7wrn5}vUC&0jn%uN!C43FRLDqZwNxs*7du~4Sx447RbA<_G^etQI-BD zqi^CL`Iz2Ebfeh@S@}_rE;VkoCsOYO+*UlBt5VyuHh#*b*wCm*2AY~)6`#7;WBNiKER_#xy+7EnGhYNX7c`sEDiGz%kD#X zNjh62hQ)F^qC}H9xXvzK0Xjine{c~W>+;Y*Ctnme!A9Wic#b~_wmW!jQvW3*OlwG~ zUr_<0cRND2-W5dX))eOkDq;K({)NvvTAvpJt?27MClmPNYctY#gpUzRDcO|dx%}rHXm?3+k#SE{9ix!e%^62SAkzl>3M_=?)BGCR#Z*7dirTtXi5NIF>8js)kAJ)~E2v4Sd!3mMLGInDo{@_m!@b`VI0Vy>!&Dz-sm$Hhj z%)Z$`h9C&PpJ(9~zq+!uHg^sC`^e94w43GB6%fV!c#!T?@K>T66TUEJKwlNj>%$}Q&9FD%YdIng#T3%=YLY}RpUbI-ylB51 zo2n6>vnofy+Y&POAghD@FkRY4r$14Rl6-u( zH7f)7Un00!itXOWlA_k#mo;?+d>Y>iyth9HB>sp$y6kZt86T9frs-h|{|stOR@&`e zdLB_qoD?z`q?nfe);YMa=R96YRG^n5`1HI0kekC)F6p8Iaz6gSJ=TkYCF4EVBUEiu zZ>d4$l~f#EofXS7x>t!XA{}lgQXiR>+VTB?4$p(loZs0$?SC--Jzb=FtHtc~W~UrL z^c8OV>wxCyr>Vqy`4<1<&94zC6)K!GxcxS?nAdZ?FJcHd%_lrxn7X;k}j zq$M5m*9o?jj1y@N8qqFEuh3#QXeqp_fGxZWEq1B$f9Tg)TjL2Aqwh3xxVo+p%(D>m zb93`Z;lrjK=77$qZhd6*ObjV;HFwJPGN~JXAUpqn`K;UQoOqmbDFpSRhf^UKqL%u= zy0sEZnsuAEC_;<4QH;5AsH<#Z*tRe$z^3M!93BKtGX4Yz3;~}{=4Sm@-Gqkjj{yGw9 zfq><@YfUQI`?r?5O6R-TClVou%3~VJteXS@ z1|}M|GM$~m{Px9nEs>h08{bD0r(a>?NfOlaoKvL)UgbT_SAORBnqW`4B`u*H=D}nU zE_NxLQQ#4K>9syOaL%Q{R{1*H;F#|uOMdl%AV?gC)fGZIBgC@T)KpGs?};rLkk9!{gUt-81uM!RIHQ9VWkC2#Q?PG*dlzx*aBu$E_H*8N2p5$>=s)hTOahadb(ugE}|Jz>eRDh$%1LeNbzxqPoO37pv#r@ zNuKqbKL0CEay6Z+&Q;`Mt@C#7CG_GcI%M_*^qH&8nv(z*Q5n}Y}zM_AtjR`b}=7cSCc=Q?E4c7^qQh%Bl{M*tSCcWc8YSz-NY zs?a-`#`_+QZ&4}tyW+bppWm}Nr$S(cID%yZ-}x%E{>e7SX~+xw*N`;60s!YOiHmZx zBeXAfM`DL;0CpTf9`Ue{s@_U>XP+Av#WGICFSh%laXeax;(htRB!9nlxb@_`$#@N1 zV*LC=sKz4*b|S+bx#Cs$UVry{wq?fY#b)b&2lH1Uw(f|bQXE?nGw$zixb5%Qqo%Z) znmO044Z+iUdsj(DtRj6sAho5X@GhwGPx^K0XVCJ{0IEtQtq)o!TZQjD8&_S>_;fPO zOY423#gL<-Fj9YiTr3AP@VIsq9R)n~f2@-aGEMJg*!q6<&yymzmml!W?j7UuG zRE{wJG|VO!^J?URsHPVTSq75P)zDb2>6OM&F_f_+Uk<66r1D1)}OqVaU=%434OvFX< zw0Y_t2__6;!~hSOUu*d%(eO*3!v)^)9%Pk4rE)S z;y%G$_u|6E6*K_;auJs8-eSzyiI1nWS1y|4f1Y6p8xJh~<)-67Tw@WiACyOlmjBHt zi{rz+GSYf1aqM(epV>M2+S#k3SSm@5sgJG3T1hT5PG{@@q)*|VWL{;RPeo8Pjsg^< m?!9EzPY~q)+s6ry{`^tE$Bst6AjGFV07Y3fnM$cILH`5UH@U3< literal 13264 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 diff --git a/tests/visual_tests/images/marker-on-line-spacing-eq-width-600-400-2.0-cairo-reference.png b/tests/visual_tests/images/marker-on-line-spacing-eq-width-600-400-2.0-cairo-reference.png index 853ea43cff10fd1da1db797d2bddf42766419867..9f2e33cb3ed7621e4ff098b01db98ebe3c412b83 100644 GIT binary patch literal 15226 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 diff --git a/tests/visual_tests/images/marker-on-line-spacing-eq-width-overlap-600-400-1.0-cairo-reference.png b/tests/visual_tests/images/marker-on-line-spacing-eq-width-overlap-600-400-1.0-cairo-reference.png index 387a35da6cbee94a4eeaa9197dee95b3514020c2..d02bd8e3ca61878bdcd1a1ab1b3c782ea3e70387 100644 GIT binary patch literal 15287 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 ZaWm3L&!F0=3;rM%ASb0PSs`u&{U0#@c@_Ww literal 15310 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 a`vM_1p$8ff^u{d!@L5(xrdG-{`2PTAj-9pu diff --git a/tests/visual_tests/images/marker-on-line-spacing-eq-width-overlap-600-400-2.0-cairo-reference.png b/tests/visual_tests/images/marker-on-line-spacing-eq-width-overlap-600-400-2.0-cairo-reference.png index 256ee1918fc18c1dbb57926696696e926335a640..a7523da64f32631027eed8de6ce774daa88bdd06 100644 GIT binary patch delta 18382 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 RUmoz7l;Ilkb+VSB{|5l|iLd|w delta 13165 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 diff --git a/tests/visual_tests/images/marker-with-background-image-400-600-1.0-cairo-reference.png b/tests/visual_tests/images/marker-with-background-image-400-600-1.0-cairo-reference.png index cbfda129c2dac164858ca26d17f128e22b0c1d5f..e991deb99f951b76a518fe323b26cb45d71ed886 100644 GIT binary patch literal 15627 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 X<0zH^C=yL62FS~(NS8~#`|y7N{w6!9 diff --git a/tests/visual_tests/images/marker-with-background-image-400-600-2.0-cairo-reference.png b/tests/visual_tests/images/marker-with-background-image-400-600-2.0-cairo-reference.png index fb4489a655d50a5f82d55d83e42f79b90bd38f5b..a4aec0749828e07ee0bf3ec8a0b940c5086bc2e2 100644 GIT binary patch literal 23048 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 G!2B2GA2?6| literal 23396 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

L@3n0Q#^&rGO# zrsC_hM!dZW)3dgp#z8+{-|vhyTCiITzNo1V^#TN#b+Ncmcg6J(!$bxag^iP|z);?daQ#>iIKYnW_xd3h zK2!zzJtQE%d_pB)`qk4Pyt+92q%+vA<}`3?(WG1tdK_#}t@HL7R>p~EdR4}x0pUHc zQuIHehtLG4!D*x5^$@K2yUun1>&?!TY%^XXc&_ld9_xMxz zTOjXEM}ce4Otz9I!CsY{7#}Vt!2Imj6Jn^SH=$Q@DLg=cQU33-Y%z+irT+U*fQvlN zi55OEbrX9=M(U+MbNj8ol_mCC~q^ zNq=R>wpA62kMPsJN+9#IpHHrNUS2+_a5?dB^YG~~$HL4jJ1yM6G@VtX`cvNXed4U* z22*GwATi@@#Ml*g;Xn@;>3!~TmE!IGWBU8g)jyY&b9>~nr7M_J8{+UuD%~IXFe6<# zTe6-9l>ejbKd23qGedw z1FwAFkJ&CPIi%wm+B8fONlsMl)3z5X?A)>Nw2@!TP4f3M~UCt0MVez<;C zmtkck@O;2Ge}uJhU*!+8=w744DPe-n>BVaqhsetXPsgV(ZJs{KmUp7|ZSBsxK8~e5 zt88~5+7~bV_FIuS|MJk<+hJp0H>__W`_=HC==Hb79~3M66}z3}R&&%RbgqobTg0UX z8+YH{D~(Ew&Xx%k)@qGffhgNY636KK(vR+anVeQ=Q`hR?h0iTf?>E&1;K{50P6PSm z*T%S9&2`VptRAUsRI3NUKw^1r-anc2H5e4Lc+MbBM=>#pjQ^ifEObG(aE z&}CRpL*7@)wuG$2ih=&q(TC|)eIDcJ6O{7H!E7cfk#uD2VZc$aN*l(f7^grjHHYd< z?{MwN$0F+^<5wSuLC)qS0Ql_xfV)bq_gwYodACLH9En6DtGec zQs(9*Y!zg=3KBB1*RwI1s?x6gof$3LO~ju^lIi)o6*;O z(6-*?YguB@)zikTk1FN&CGmlG0h*m`g8X^th&23-ZTnmutq9Lh%Tgk-- z=HvQ1^3pCfXRbCyD<26w|&$mbHBR?APQmZA0gVc_=jWte57ZauEEG zkMg5-`u=3Z;Y!9-`;EK|d=SzaUMslhQA`#bC#E7R{Ig%UbNl3@gIc z&UbxpP*HQU8>QyGX$iH*NS!sl4o5<)9WGvkFqhbOavT7WP$H?gQHl>8hu=75h%iup#ic9x z(>moyal-v4!(>^fgEwC|VN$onY?$ou%U78L)>PXh7Bb5x!OlT`#j>Fm?X~7BN#Zcf z6tAa={bo862yH$0qum>4DY3ZQ0K8^p8wacGB+s&R#;{&a`YUph*-9z&b8yjY;=tiA zA5Q0rOJeyHcS{$kD!s@vFfB5mU_i@_uWhs~+~#pYe(AUCp`Lf2WW81S2vo-_RCVxn zSi=sy`%@i{3BM(Tjmz;(ZZANsAGCF4!<$1@C zXAhtz98mB-s*MYks?Jq&Nd~{6yN$bL94o^U{FXT!gA4UyTo^{w{S#G!4@X3m1?^klco2SrT$Q~N9+%7Qnx@Kg`@xeY zg6xMcZ2~oR3~IS2X}X6M_txi<5@T!5E8fvu)@u8(FMj8J8&WZ58nRBBivF%x!h8ID z=lRpDX<8N3njb#+`um8id1wR{Z{5z$Z<_CYWV&q?)!_mc753?O7^AU=C1WMj1;D={ Pzo(+0`Lb3H681j;MOaF5 literal 32087 zcmZs?by!sGx%cGB^JMRx?3uM@ubDOT&YKh?11(Y_1|kd$3{o9!brTE>EC>b$W)=bV z;}wmg-}R3dI*jzpG#*cJaWV1m@Q8>oh=_>D$jE4EF{r4hXlNeaG>nW?)XdBb3=F)y z7!=Gr%*@QxY&>+FtW@kkMsD`U7at#$q$CCdj{qwlCk?j*H#awjAQ!Ws5G|jKygUYn zumG)q9HWpVp9rsv3?&^Fo--8k`Q1KS6~!XWfD_m6xU!DSLc(F zQCBC|)5Bnu)MS&=l$4Z|mIbm%>u|}camZ*3N~>yXQ-eTc21aXGxW1`~=u)I^U zrdz06q*{1{xW2EavfC>+AE>@>pqYC}aIn^kI9(Nw3^zA7^AJC1xQDt|wux7KV4`+n zq8!LC#W&bi(>vET##6@+?)3`dn*1CRkQ^N0017Dd^Yin~wDQZciHLDB3@LF=4mJrZ zwTj6xi!9HFgTjiOTvGC)OWmy!YU3-s98wV}DJhQW$h5kE7uikml?ioqHg36{+09{j zt&xR=g+8zQTUuTG%Z38qehjbdDeX=Qc{f(xpB7Ow6L42>GICIot>hiU+j=~rip*A||DrUhYKT9*!%*9eM0ow%8IhKu(ko&Q`f%#dL&4YxQ*JUvbaQ)*7F|)x`EIkF5jse z{QIbrS`dO&)NuP*4;JnASH6F*B<#3rI^=g|*BP;Kh$P$Ziz3;*>rZ}VVP$(Ijv%v< z?ETiNze&+J0-nM<6%w;(d1@TmtK}TZ04@d~RcdNi-maPTf$7fF^^bI*b$Gf;#kr~Pqq}mU#@)h4AuV9t@2?31t5XCZy z`j`lGnR+Hfe*N7UUd(ysvX&+a-lo;)CsRWfccwc39jq^i9y^CQW|#r3Hvj0DwG3eW zXZL;51aZrjR%3PO3t`I=VT;qo1MK8Mt{vYT)CS6Y=HYTVr_>-))&V$B&nGM_s9|Ca z?XLP$hzS7#<@>zuuA9z+FDk#-Y-Xg_O$EGj;(gUR5_u3Qm=t7Z=|-V_bw?+`s8Kmu zt&R0>%fyC}*#Y#zD747&`4fD;@%LO@%gFe;o$*XxmM^o}n_h0z%0=tz;|xwAejfVz z`i$anJ!8*$w@UHj)0WZ!SFh8py$4>zGUJ~mE02w7X1h;*27bz~M2fyg%80(zW)nr$ zAX{7GQRLW>fkTD^x10I`LV6`hV zt=&cL5M56WbQzi3wGJ72k!g!R`Pry94b&@;+U0q0!O&HQ0?=UVSms$jjhoBT1;6o3 zOxcZDm8i9AnqOCKp7=-H?^>Fg<=2HFxc3WzbcM~|y{&rO*N#A_fo|$N$$!2o2Lt_r zYj!xS)2(#Nd=8*ByumyTQ6qv!{w99DzVJ_5*zLClQJrZ0&4)@^p>q9Piup z2?p?VUrZ|8bxQ@YO?2QSO`y1Evp(w#aJ3X*KZu3{skGbcu)*YpTTIxd6+(H(cg0u! z*d*bfU#&#Gtu{!{GX5K0>L$BXwJEZ$AgDiR@+RK(m`u~<9iLDmW}32S_ltvJA>0tz zg19esPbo{uERqX|8=V+lBj%!UbDmaWM<_W#PayaulvU0cRO-89W-e*&?PI-7?MRMi zUqwt_+JFa5cv&IG_jKv$6)o*75VFhhx-;LDdU%$IOOq3{MHphCLaFud`HePyU<7j$ zy(T4P%iP>b!mUP#W^W}qfXyQq=t%(2eZpt!$TUAGhSj!MATh&HQz`|+Y|27yhV^k$ za02(b&l6SM0JgM3`H1fw?SG7F$6NBaTE|+7;9;o&L)|1Fa%}nyeXrM=q8=kx71bqi ziDv)IJ7NIuqxeQl!O2f5&`4Q7;1%^zY=B1LHTIiN)r~SZkJ0B;SwB>bJ?!ZME{OfV zq3pLX-LRJ;Mg_*Zq1A51!q*MI$w(pRrUzkXtBiin-9k(cX}4PlEH@8gxpSp@;bbDg zK@*k}wXs)ja{4aT2hpP@ah$`2oVqB3H_~Gc5ts1CU}*W#!;J`jdK{}3L&?%9TiR*e z=YpH07TLuXD%$|{n)j63(Vcd@F77-ivVZR+Az4UwcC)A|h|$54cD};%Z-X?^msd;+xdxRQJYsd}=%rB}u%n%_wPkBFlG2P^O?nJ&IGf@xK>em@1|T^ZR{lq468a6QgMyF0WS0q{byyX9Xv~cgH6`EzSOKYm(+0TDH9XO z3JAq_=NsD-bcV+V?syS;n`-psL^{`T{9;>gX<*eXMpp~hdHGWBqS zx)*q8xsvjMZobPVp|uAE1RU%)|K4aR$)ivuhStYlmzwrDW*LxOC9p`k0UB9!rT}62 z;+&d-itIDC=#KBcj6;e)qx#E`M)&3`1nPQt^7N;82<(pR{P)X{zKL4g&j!$u8p;G!y?j1T zFG<6tq=&adPtqUwF%=#*L3NTwhe#a*IMEY*Hsfn3f!=RP*$P!D<5${@e7pGY()%&- zcwFN&^5+DgkI8zB!XRQ|@Z`e`<4}nP3|lDmanId0bz~4ji#I~>5lN(XiyJ4O-*Rp+ z+M+CuL9BYL#o-?i_~1YB-&-W-DGxRI9iN~0$~n|JskX2{dd|C6WpmPHTifq63>Ygc z8&RP!noN2!5V;71D)nN}eKrWPrujikD-T|(q=+Q2l!MWv(~H4;mH>+=?hO7p?4vC) z8)RpCuww=va4mebJS(RP8~i6CBiIT$O9_OK@szhu+}5)rg#-3ucGd!!>HO5)R#v%y zxteDu1p$Ks{k&h`8T1hNQ?T^?lcDafjribs^wIw~E}BOX84ty$8_Xog+{pqQF)9kW zmI3@b_!wyjVK%?f*r2ULInmwZxmJA_KCZqV6MP|6FcYxRy~X#P+QSYLSq;Z7^zWGa z6NKP;C~VT&hJ1|47}|LNn3eRJ!)$QXyAvX9=W58=(mfzO-FnT|C#GQ_#>36KOIF)K zWs*{W`kv>(Q&J7}YTz@iEQ}jP>$?uR*PD){T{_DOad~%e^B%G=1U&O%0j@zM853tV zBH-v5?Ft5VZd*7W)CfzvmYj(a-L;x<4e=pG)WLJ?Vr&FcpCd!R2s*JCZAgAW5H=*Q zXYcNL!GKme57&|!l0A_PZF^JGThHCTe~D7N-d#6*d#9BZboH}5ihJek4eQ%Nw=S*; zt(-gkoayOj*SBsuN=|l43Ya_XY^;%k33tKW7bvi~!Dc1YAmaXdFN!Mu((CFfHN0Ue z;V*>Y5J@;enk@UmXj4OT=6PXH=XHXvD-}?g!nx^20K!~sGQ$==JmQ-e_E`GOCIIi`9)QiC@_^48OtJ+DX8SckC9uMrD zt|fF6@U`JO3UFj%SpZve%%Abk(nM65qlM_{b0~VEB~=iEr1|d`^!0P+WNJ-Ky#G0-d_?iaj`>Nwb%;`p ztKLtc#zhXi<329B-0Gt$F)F2aar}e4xiu5*8qNwpF~^Dx+A05Q&RIK<3V*U=Lh7CB zNo0!R`xD**iA3vC&t@v6qGF?^Up`i%A3xVRH81ycV}={zhRD5nA_zKiH6E(S%)>VI zR6rG~$BNCW@H}PqrJT!htaTy*8KtOwr;(N4fA_I`&flhKe-#G8#&1b1qW97Sz8u~# zpP<~U(|KhMy?e_k4M)#oDT2LgW!87ii1w;rdVTYT%$4CM2_JupZbN1{mM(V|ure3@ zY}XCaWo$~k_hKCg8&N@4Vjn-(RZZ4l662m&@IerVBB7w8(_xp519btyu^f}XX7z%! zpS_mnuRFNhl;C!Yv!~BAEHx}GHTX+H2A0=-J_V6{A`PPR(I~4pQUP!FFb2Hs+2Gq$ zu1_F1wuxvBO&%9$U=vpaz|lJzCF2ap@~`BGP9MUhc>D$SM)+E=u#G4AdOd+l+ev)S zs*Zz!UGF|kQxPeXObxtoRS!Woc%|KWZLWF_NAGDMy#8MCK5U;7UY||B+&7#mc=?vV z?U!LYv>)U27p+ju{it$n_&qt>n+WH^;|DYCtv63YPUmx)lzK=3^=7BgEblm~Nw1<_8QTv^2$CGG1Q*#o-em+R~p(?0yH zR1-X2YYc>>Iol;^mOniT8C8gzg{~&kcCd-^kAf))PJT*-6n+Ybp^)q=P$Ce0sbz8g zGO?p(5`tp$?(V2E!Q~fpdzMte+*mhH^D^Qy=6NqRC$ZRb#js$rG%(2GJ+c`)*T$Fh zpiOqIE25&kokHZBo@-LtSAry?sP6QF8F^3t(*96c@sIj1 z-ieC6{i&e0XLCMcH6Ja*V=A(Ry{g%<@0u-5@~6dLvY;lf1ApzB(>%%yF1?$u>H0pG z(Y}tKeP%cB*#GHEo!<#5y5OhB{_eI0b93ZFq1ixHD1Rd-#o%ED^ZuV;$%uU(=3tB< zw&01T`9gcMG)YvK|HokJqxvZ1T- zv>zU`fFki2Ut^pI*D3%&n8C4F+8Ra9)pLej`$hwvrDb|#9%FQJdm#?C&m97P@u=49PRl3 z49KY&fB-QdS^Qt=A(=E7kWG)@9lr8-yisgst`f81RsatKrDv8vGNnufvKv^Xdh0>n zimMbcT=Mir{@rLeTP2aW;#Vae*@D$vJqi@ldNN*1yB+OUjs3xv3Uwq`)u64@qEE zLE@G=BL3kDMp}Ng^4=Y3oFnFib8E2?qxKpz~;8jH*6d zh&U}@=$Z|rJ$Y+&Tnj(xATUgvGsX^glT_R;DE%Gwh6ZG{g^N()`roKcJWX=nCo%D4 z?7SaVl;5g9ALiVYm`pm_7z3*61$iD~X)^~bexU8(mT|P|DWIoX9r4XOBHq6Q1o57d zI|y0c8@ht5wz*~Ai(2Fi4}Trr7>wqK85}Gdo|>W*Rwntof~)-Rca%&WQnb(L-?#|c zB)i}C;E3I~UoB^VSw8ZvkPDwovb_`A?RI7j+0$Ph`JU@rm|BF|nq}}&7%9FP{UT)y zd|iJ$y5xYT`npLAp6UJ`)XH611w3^)yUiEe=6%>zrK!vzjr8?sv5JbOqF`C6q-gcQE7GpuImd+WeX} z22!|PTi`!AXcPW%E=}+F{0C`bYrBT+vRuSPjPX-aZcByFy)%J=1JB?8V^hqWdI-hf zA-HirFd&Vc-!Xspt0yKbaH*ZBX>A)(B-cB%{GavxhioJYeWZ1;VMTJJo2*lFfDnqz z6?(oF?z594@CWwZ_FEa!m6CH1Wtgn_w>cMnYO}+yL&h%tUrI|oMn@6YYvi>xjv+o1 za`oJlz&*33JijFY-Gd*1uODB`e4PV_%tqsWnb2E$G50B>!jDUiS@_&{ACg-#XX4ag zOoK3}I=CZzYKC7gA?dt#o$lU(s4~SsX$hTo?|xlV@&j4zXpjpf`7bAsK~m}g-*@n& zV4mM6v2lg!h>k+VKc?60l#DrcAcV%Os>zEjq2xj~wj@Kot+{xTENPxM-v*hk9FbEC z`SC=My(W2yuhCFBDcG;MU__(@h%q)HM2v;#LIEC5hPNAxB#-ORSRDjJFNQD|e;=Da z?RZLklbl{QSHiL-)7d_0L^tKgT|}CIC$f||bko?1VZlr^ZIk5u0kgy3A#)T=p9;H3 z!iH0bB(k8&S$k2?_yWeCA7P4BOjIL^;F_K4OfHG9!O;FgESQe;YNhP)#LOHI3-KAH z?MYLKDqFwx0G?3fOcfTuPT41-=sVuRT#6zX^bQa4;PKoo0!g9>@BSF{Ha^MIpiiP4 z@|1Ug=}p_;7V@|Yc`lT3@0MR32VPd)A&pJ$`qA6PNO(xlQVQO|`k9Ilq61oEV$5FSJuRLCs_5>I=~ zr$p9$@=pFm%-`E`yv<8?xiB9|_bhL|7CvJ^aKPOGW!N$(-+~(4;p1eG_Wxs{!n0}Q6>iFe0u|n#z?;an)p{$5m z7wggfK335T_64bLYiamZXDf+-;k&6{H8qKy?^(@EX#>h`q}ks*E=}J@h|SF$qkXoC zw)uRJBlg#{$U>X{v%l;(BArvDt!8rr%74RX*p+|r3-RYwRU%sp;KqM!b#s~c`zRI82Qb#qWh6O+CN+<>haG-=#Ck^qvgAu?;T2$(OlgmV>r!s9-L+3@lQ9n@v1Yx-&5Uu62m6#oyryfO+Y_^z8P1P8Eu~O;xzm#}r2Ao15G8-OX+)v^sCrC2yj@q!2@U13K_z!u%YH8 zM0UM2ySV&fraq_OQt%vt4eHiA=?-E^@TiQ_c&qzboL`Dl!V_#BKnJ4;aW=kmOj_lP z5P9ikr}GPvMI#ER+Jj}-Og=`MIp_O$Vz&A41gSnw+Xf$ zH?%qzJ9Hz*A%S>vadvQ&@;kU+k{-Aj(;2Pg#6WcBuGNEqyec;(?XE)=1|IjT z$r~sG5FEHd|1igt>JE=1|v!;us6_k-XAM$nOX(@9bf|aI? zka!$-`wd|^BH_@~77U14;ETwMnF9}EFlo7a{*=1jV0~GH*5QpdbRlKPU1$_WS`QC= zO4IU*`02LHX#O1I_Na_XsAFo0gPyLzCuSoL-bzZ5rHHDjw)k)~G8`|#+u!=up53h; zcxH6fn3XAg`Kjy!?7##Fv4$7Mc9=c!-n(D2jp$YQbj4BH@hUKwut!6E^yZqr&I5;Q zx6;X{NUQ-m!VQ%0)2!yOd?ro-@iRI8+rb+P_($wJYBFc_oD(1jyJ&bE)XpJYjk)dr z3tKA+>c+c#*k&^ZD>uMqb%X48n$}dMJ?^JdK0=Yta6B%s*g$$s>=eh=Dy%dgLqxi- zyO369_eO_#3+<9W^P`_9FF9Ay6QjkwzTRm=aSAlr0%m)tB>?;#0xF|(i4Mc4f%5RP z-@HcmoD)4^moo!7Kd-0BmG1_QyTS!0_w?@n6mdi;t8D)t0Nl1boaQIn>8i6zAVF&g zXCCuyIf0COM{nn!gaYOE8(V`n_jwW5CpdXX==lNuB5D^HRx)wC2TVNuz;=cc68?GK zu$lc~w%G&y-)&#}e#VbpVOgF3ae1bstoHNr7q_KS&Q7v}8yh&J<^K!mC4Do#$g$32 z7W~>t5Vd-wrx0^48)jMT;tEjA{1%)cB=tu(cHf=daf`7A(Mx+@_LV27*j-kG1#?GI zKfpZkTgb?e2x>%-uwt=Q;C3fa@EEsaUmw{(!RYvRBb%o6=%&cwfE`=x`zRpKiSG#s z-rd0=U2#q!$o%vOoBMsvX3lKxy$|;l8L2~4XdcCFp^1tnHcy_}gA@Uig^DiqLNxvTN8C;%g+NCIZY zJ!mQ=ABSt?%XHCB2!R4Kh2`8kf>hE1>+>T|DHTHqey-SZX;@XYR?#z;;CS?UXO0@3 z)hw5sI#!h`;FArChq{R+nCiY*EA89TWJ7RSVm4?%q1$2eKcs1ww^s5>ckoic-h^sGwS@PxDval(pFK)GR`I zZyf$y%!uFX8qWbo|LqKMHSheZj$7Om@~X!7kX*zqd*+jMBcKc$7+dv@`}W@V{hzQ| zY!ozYH4`jiSEJ&<6V2;cz5nRUMf640ET9Ib*$Pv1sD)ZBE0@lx zqL%FmQwS8`O>o?mGjtLzJONM|8uD2pRQvf*ksuEG1qycHYI(m;_}{E2P?^!Zuz*^} z^23%?riHn~FJG6PicUCijxkf7g)qJ>4M+&&WhAB_FU~%3kU58_UmohOCfw*T|69Me zKZodnlzgo0W1Z$II@N*+7S5!c)ye}ht6W2NWwaVEnwBeMs%)Mtq+1F5A-7AWaB6Vi z{e^!xlxSiZSEj(u&#W+!SH^4m8aS#y$ZHFEXKUxa6U8b#$QV1bO7p3`R zl+|kGmanR6xWnwYV4kB^`Zmv48ve52!OIIz{sTk#rQN=4u%{P!cgK(>81VP$t9Ccg zJ(l3EeBnu*U(z@!4-tJl`cc=J_J50)gN1lju2{u8u`y5xjo-csJ;EI2WDZ5$YWB{B zYUTCZw@w~?Y0!mjwI1P1ZzNZHYUMW@E!969!3egL)9d;OII}tn>UTTnHTywyfC^4S zl~c{#H9EfAD^q8%_c!6_Pmm)PJOC&!J>WAnNb)Nnc6{#T?5bT&bpi$R=f|qx6+ONE zIHVQ6X=Zt3vrtj0H0!A2ST0Ge8}}WjXVS=ZER6^dQOQ*3{=;&Ni}F>9ntkj%J1=|b zrI^JN3yU5KNPiYK{sE@`{D5_b@%Yk+Q@2V(RmgZllK)=hsv22>)S1NNLK=G|`q7I3 zF1P-q6RyR2JEwcR1@OFR*;A!K5WDgT^-oVYCT!t`BEA_l`;y5fB;h(9mio&J?|dUp zion7aS}HWRxkLmM$cBFdX9=o11NoI&0wVaDFBR@XB`i6;)Mj`D~ag z*hS4gksEp5K5<4`9%?aP!Sm)xFUXM{8;;&ODNFf&aO?^M*?-L!x(wP}F%|{>EavYqRK?aAtyHhsLypnOUBB zUcR}RUas?oEIv4dVy^}71>Y5RgQxY$T=NqMj4RJKL0`diPxgM^c+1?)0pG zDLl`oZR!5L`Q|Pc#k)PNJW#I8Z`Y4``s;z{)N_3AnQQ-@qb;%b>2?ioOgPa_e6{(=y-u1N3*{ z%f=un;Gx+#olY6Y7`3z%142?@OngrRNT8?Nl8=L>5T?(<<7WfI(Lb&t$HF`~PrL7b z?eqP1yKocM zXopMgKO?7}t_b|!U}j(0WvxL0K@%SN)HInt`n^#9PUsKAL6&g7ReQN(j)@yPp{#1W zCzQkUrIV_}Diw=4)4mHE#dy7a^skBY=LSERJq3b7K2UK}af(Uk_t90s+kK82FNC97 zcYs@KNd8-7<0pQqAA6*oTs?}j8MLPHTmKhS+T8S29JNqjDv|_!G$rk!6awBBbj3TV zwXJcJ3ljy8Mp*Q}Qum|N-uI)y241hEdGszjE-ncffXqo6RXB3>>DVUUYSZ!hR4j-) zrLIAbr_P?d)hHDu)N-2tNW%dUnSGUJiVY@sEW61HKIUV*BthyuvCGcR`bq9xu-F`p zTM#i+js%IuVSsx&4`QD?QL&w8LEK~Go5oYq0nmH4P{jP1se~$9`GsU?KMyqo-l11) z-x0JDG2>WDHFVnX^9G1@)NGPL7xP$$Z?|B*HAvi;$P{*ANgMwi!6Y>4^ydla6ynWA zov0c&o%ap7x5keUE6N0444X^51G{L&h^`UtbJln%%t~~v zZzB#-nv4VjHK54-?2Ev5N;w=6wlQC#Cel0~*{6`!yJg-{NI2KmxmX3XY)8N_qQk1@ zz}APj=G0ExncU-vgtY}7Be3B*{BGPFrC}Ghbp1IyXObZ)*X^+7(PB+s-H&yhqm3PT zPa!}I@9I!H0 z!$Q&^7N`9(EX3I*QyrPXwDN~H#OE#<+heY|@u~?=NDe>*lXA$s$hF6AAdk5)0|^a5 zZ__9U(H?$hr>zC)>PYdi#&kS{0STH12R)rgj9?Y}uUP5FYb0hAn(n*iBu6k469XtG zQIRjUW2Cq_OEwmx1IXeo1~QNx>C0oyi3E4DA-`$UVM6>f{JCQCl_R&lCe&{`xxC^-n$&!FhuuIGvlu&6 z44{GdV;T}|Jh3f3(QW64=@1hso5*-?;Iy1(%Up_J3Vk@ptkFbhg&4j+NINF8zr@{I z-Ae~_ilst8_aiNiyZ{U&;0tpU{+rmTl58Hp&SwxxTy>bPthQYb%4B;Sdh%+i%j-J^ zwcU>(EC}n4!dq0ntO!N4RPxYj!Vo3#_hI~o-6mX4Tm)|Jcm{LkP_e@0<(uksYNQG8 zV*FAM1G0Y@W}^zDAt#6YBM|awjfuW6XorRDx@3|d3BW&6T!$ExZ8eWp|mkC^a|#IcTszGHYii7{V|9lQaN44!@xTa%K# zhdE_hw1{J-RY)Ba$99DKaP_4fv2{Oz6g+{9hH4#*Fd!mv(57^V#uki6yKpa&A;K{Q zO_AEvQCE(SCjn`J<5&oT$2pR~4YFt`g|sSKZIb8V986SzT0JA4Gj1f7Nmma6nun10 zKyZDE*l=D02VSGFRtXg#LaKULiW-mTVxv_t5iPhOD;{i=c&4xAcE+7ymh-3kvlF39 zPlQ;p8+!0#q&^R!oP|z@ha4J?^$FSqSsKV=%>{UgBJOOiH|hx291kQntt9rUklD$E zkRS(yYVe~(V&ZD$Rs(fa{4`NTI5!Ywm(d=i!C3kcW}^aIuYo&SZfoG83<%E~_SQg; zywBQkhpI`Hdtcwrx57LBCZev2qj!e%9|r$Q?A)9B6>5)QA%3<%n^a>vyzOmoDqI!N zuO<}G-5QPAm%^h`B8eC%**shTi48HgM_x=6^oB-~$)f_=d&Z?Fgi%9fULk zwFA3ntLaa?Et~)A9w^u<*7+^5uJUnU!ly)LRbu~@5#F+1GP6oO(FOH8Q&)-`f1=?) zj?qrgkBkTV=N0AG3l>lQHuLJki@hXm zk*O@0dL7$_y%C81SL0#;>K*9D%X&IWdQFi2Y@quvCKf}clM3pzo-fp*_bg&4-lcn* z7)AlwthQ-nh%NZ~b=X>mh}e%f3>!NY2MarlNKg>;;ZF1$@4)Zl?Cp20tE+n+9v<&m z)#i*gX97E!C;n?WxVa`2tSX&L|7?0$y9p%xYtT`0Z`vlLl_B}k`X5);zt2X}!Zv-O zxYwM7cl{0lXW7mk&`a7^Z=M$Y%htHI7r(X{8t!^lZIGBF^FI1tXLLN&-2AN7dAe9O z3=V$$06vz4FE2}E+qw{S_4V~lCL^ivfHVV*`JUMH3>NuFYpL%=85zf6A4EY9GUFp0 zBGLnPU8<3%FNMui1XjNB%HVU+6I_C;_W;#<{SZ{;R?b~(ft>mQ2dNwh!`gqX49|}% zopBc?RF)D)2JeX#F{3!3WncD46tr5hY>4f5x@d@=s>6S~O2hZqba|ks<9mWLeQCH&BcA|fSAaC;?O zYw2;+a@}3HJ@N#OFDC#||wCglvGhXsxxL z8O>4-2r#{>s-lX95^-9ANnDa$)U=YZmd(d%45^P^oKGK=8{cT}IJ;AoS_d7uR2Id! z4vEimRN<;cI|ZDwzqB7Z*CNeEwol7257_-oPdDcIZ;?QJP2)(Jh!)(u>rVRn2 z<_Y8<``~ad9eF}13o08DKiK_Z5;fal>56g-Y5Rw zkjo~-P6+{gI%()8JI*PEmJF~~miIq2S|4Pa@iX)I0nwqIP~?a9b~?S+HjmUop=-$* z&!Xi0$m;!OHN)?iz@uX{CP>&B!axC$Cn8|Ng%3epvEW_s-t%!bh^7C$AV)W^>5Lu7 zVYdan-lNpnC+K!f6oP=~S5)O+<2Cf2lxLymjh1%x+9e+UAf%1&PezLC>-l~;GCw!> z%t~}NiXvu_UnY|Trnetb8T??7rh{MW^lQEL&IwltMj%ElJR7}uha>UuH3b>XzIBlJ z1X}%&I6X3L@LG&BE@d~~Ja10y4VIro%fSEGw&Jx4=>qQu zY~DHf^cO-SP5A9$I{W{y3^^dGxM-rwOuMOE6a_@=nB8i-Vc;R$MFzPYHH=9EKG1bFFDa>GPH_8USS3-;)enubLvUTjhJb6|$ru{iMftiI@n3!e?G258`) zMMes@?ZVe{V0$p}k|=NOQp!8x`-z5uQ1^qke?^;4b4!ptscO2Sb(;r3;%;$JOL(Aql_#@)M|~{et$RaRZP3-6C}!Z!N}|NRA=E~XT`#5BfHB$3RX3M&#`pFq z5R#Zn-gaSepl8Y0ywUi_OAS^^E2JNPOyZDEi^R%aCm_#R!${*eA6$)B0LbIX-BlkY zXTUpF#$fc`+_4>YB@A@V$g$1H@gD?2KP($px*PlnEPRe^nk}G!dX}`6xWVBxu-eHZ z!8pl3IROV8MS$FR#S5`D06&NI<;lpXayek5m;ad3YTyFC3uN>ixw%sebw4_CNkQ%4 zWB$6zXn|DN!E=fN59aJPH31=yxubb@+^G2UM*_4H4!V6gmYXDj4zcv;`ajnE6A%Or zh^5nA&4kBz1ts9!`iSG9+vPJbQW>nUqho>iR_QT?1vR%{;Nc%}U>&La5h^W=U8KcAc>ciy4af;nLvHr~BN!>m(H)|O zmg7_aNH3OPWM2VBEv?bT8a-`o=s!v&Q@MDdeXTJIXrDj!fva9&DF1M1bM33--xj}*4F;4lkc^H(3*h%pLO@YdVtoh>F!D?AN>k4_u zT{N?sG_h;ZoAk~b(Mg&Oes~K(QSYDSCAzf9?)`~&y@&J! z`UG2edwjmpuCu=4E0r#)GyS!U>Pidi6q&j7p8gv-X5BJ2Q`||E;DkiZvPaR)nNy}( z5%0tgkFv;6((BAfo6gVf1L`Y1Zv;C2;d)Btwj9q;7pGW|2PjTOK-H4cLFDNa2I@6iu&~WAS8=y9ew74!}at2x=y0N z#2O9GF3koYTWAZzCEdrXw2inN4}IsL`!V0Dk@i$?^ ztO2}H{&qPFe%(~_xHZW_7|+gMxk*dGm{SVficm=(1u($Qn!EFw_X1^o#6uOQdGwI- zM`l;Wz^P$JS03lyGW0IGJ}LU*?XbC@$Bt5B1%hD&kVqf z3Hb6&A3NozL{)cob$QqJi2HqoL2>ucYfR z=>527`2o0n;p@ONZ1CAI7V#L94s7--`vrtvLfi~lR_C7HYsYb9LjXu`dBkRJlClc_ zRK(J-4g4RifYEZ~dPN(LA`I;>2zCEGtp(F*BdK3;6gPhI5{P?{NjOg`Q5IRfTI;d3 z)HC6D{b`73i1-{bRBpS&4%8}gC{$HyvT;UFG>al|bq<9g^p zz4&*N%v1~Ul_k3MDXUOnFxJb`C!t@gA6Ahb^be*TXm-H>tMh-9k;nD0>kQ+9 z4p4_AzMyM9tq+UGV4|-wa&>I96UTVu1t0ax7xJ{qBMGD<;|J>NIdOq1T46LsTP+YB{RFrA(R`f@QA3dpSHW|OglR8=Rb43 z?EYT5^b94k`>$-W_aQ}pAiiPeM-<;_*SUE|J6C(`(Eka&<56c_UU>2f9nY0ak5q2_ zY>NqU6VDTEf6@0k-}ZpHMRN9&OQD&i4KdIp)@A)Px8<-vwBv>8PrfBb8#CbN?Sfi5 zRLTF1ksU-3dl6`$fh!!csX2ahQ;fZ@gPXDIfDF0x)-P>}uXFOe%&7{>C{1>{8)?bK zZF{XDOI+hlL-HQpyNSrH)^J=9`R-<%x{*&bM_gZpa&@&S7D|@=e?#X$W6R$^+PUqK zQ>n}T_*so}5}Mc>B(Ib)!%lphlMZ^*%Hr#)c5VG%wVnP<$0@dNir!hgl_b!Axg>3Kn?hs?3USvQ`>^&=rrKM~ zfy<$W`{j@9UD6tdrAITA!yW_A|5Mmo2Sm}gd)#z`$!Dbh$w zEmG3Gz=9wkxpX59N=t*%((sP=bD!rt=RW6s-@j+4XMQvDy*}4x>5UPGot;`OObHYy zUIM4efu2pleWa!^0rHPZFTbH@R-L3zxXoEOl%>ntRhDeO7xq4u7ql||E!+j%9ZOE0 z6V@kv|2lhCWp>k6rsN%*Nig{r8<^b?4PuHLsFw~|$I58G0iO{cRMI3{UF*=>DiF$L*JAW-^bM%r*!)y(SJ2l~0+{Zr2fr}6yA>Wk1*>&j5d{nX+(g|w&G52c29es|o_d3O z>Exhstz<5p0qaee;9sV1E0JGbPlk9o>I*LszZ)$4&O_ya5$qKpvb2~URPXuxGlS95 z{-Wp)bdYIBa)@Y{;iDE(7ZxD-O`#4B_ZA4I{psu2J>7IbEh=3HBOBV7_Bok~t4uGQ zJluTbWu(mV(YQChNC~kml^f@IpS53~cm^(PnMc?~c z?ud?<@{3G-Ff$i5kvA&=uZ__=|Qg~0e&U9LBDp| zVVu+|b`uyy4>61&1oBqcQt?0+hJj?Z?Gr38Zk@6Oq~WA;&kJdsBs)fL)~-0&td0Y( zm?hwDbyoFs%ohh@-?M| zCs-+m=ou#j6Mr>$=OM69IkIZd`fkua!d`FL32Y~JzP{r!sNXAnEk$+FNdZ}5!q6fg z15j$+bKP*Gfe%3x_&k2JJNsYQ5sI~IbIrBP;y}{P-6S?e!$-&c`D!WbsU+&&GYo^W zgd9VTH*tr8MU$_|cusWfwnK{04Q-8QtahM;dn!i|Ob;Kr8X^!I*T+^TJcGw*z{Bvb z9)5G_Wiu8nx7Slvm?3D8^&(>Ft9w{jEtR0?Wo7M#!yNnK6#x6?DfR4lnm=PnVhK z7!1h2R1pLA%bf0udH%1(N*wsf*NM6bK5Y~(3_g(!xPHk{>T9Pf6c%E-S;vF$XCL3E zRWL#Gn4D-AOP1n9%7Y?@{1-0-^|y|8qw-)qWv4hVTw+tpe5PCsu%w;*jQoRnNi)ub z$#-NkSaLFR7?H{&47{A zt*k1Jw~LR&fXNcUFjECt>;=i>|2w<2EY{y1t7q2MO| zSh-V6=-Efbydr4c@Ym#rFQQ*|V>4Tii5{d;omTmOyP$4_$=3>z9Mv7YZ&}|2ZK~=( zKD?NtrP5*}rkc}*7#VeLM54Y;;QXuC|3LELVBXbY^IyIEpV9rQSA#eSD-U)4?(<(a z=SN*8tb>najaaMe>+5@^vixWIIznu6zC9Jvr1w`qYk1{?dTaK?>t=m`Y=_Y(g>n0( zgVGnyar=Sqb7~v?iJNg1IT*F=)Puc66^7MJZ`tyeDN{1fz})`lZ(Mr?>GgQ!!Lu_n zZKba)qbM_%t4)|?FGVTKnNpn(lQYqi2rvS;COvZeYYbq~;8S5f?{^c=jTP<~u;N_A z7G|nGuR4E6`j-6MLObY;tY8p`%#_^n90@I&5R?-qb7k`6XLdGohhVXkv|kIah1yz&9u#kj&1|!xujziBcr$1#2v7sADu(nAQx(H@HSUQ{?0$BBO;O9A-Q#L8%qx^A3*ikKrAi*maM6G@2GY`D6sy z`m-HX7;0c>epT6l<8*P@CE&DOptqi)dP({_VMjjy&0i}675*10-}l|Ex%en<=?uB6 zvm^w`c5;s}D=m&OjxYD4Yvj6$9VUhzNh%C?Wrh}sx)6T^xm8J{A?9>ue9wN(SMx)- z@`?z7+!Da%yfCdefI5a!NZ%YN4(eyCDe@?|784cwq^vE9%oxbVZX$+o@>Hvy> zGy<6TCzev=yMAzf95@4p!MWiP+R*jf7f6u4eyf`L5kXF0(xxI%SL?r{Y@_2vm^Bc< zc=>1$Ny8EH1O|*)dnPR%8MhxgG}fW|)dAN2R4UyV+>gV_@DcI*zC|t{{zMqNv)DuL zC>0VmzTz*@Q^8Mg;G<%}XIhT>@Q(5-gX4)V4A78RA0C{wx|QS+0w~C3!h>zP!M@1m zAHd+;vx-{rdSaL=MBntb|Fo$KbH>C=H{wH(m-UaURn)!57&E!d|B=I2$5P>NoD!RH7K*VY-ZITKAA}sb~G3qgRGmkkw%OrX`=hhdhV*9 z3o5;(_s7$m)=AC??#O5067-0WI-zzC@x*E-12(#qqcyif0D`aHAuuT*syVmEiUD!w z@6z!=#KYT$=!ce4>$Av^jt+7#1C6lHgZPYi4$_2PE~AQ)$=%&yo=>;FYB2#+zNG6- z12q4h6D>yRpwN#YArn`+m5gum^9RorV@ClUyTXxf2K-{rVd)tFf>N`6XIx%;vckWb zs3}V2G!P(<5=IaXj_r`BMN1LbF7a%H-CQr$?3pToOHd4~e(+lFv@sI_T?Wqo6e7^0 ztW~S=4V#v-<$;TKB0jpZdpsVW9)IqUk3H=vmtE4P-XF$L`ttp!{q^RCR2wZn8huw; z=>y&AV=YJ9nb>swFkJRAwdAj#{7}2#YGeue!jII&yIvI7jL#hSJ`U`~jtJr}6PtPn8b^Pj)UW z=waTQ{)soL-t#Q~FI@mW9o3*w6TcTpR((o-_e_cZjAUcrHp?t!Lbfx`yq`9uOe<#u zh<(+pp}AJ9GPCYIRFGG{wflq9$`*6X9~k8Qrq+Wf|ROy`#QM zQyJ=2sM$5GQsT)F_r4+E$k67#HKZT|x0E*Rys$q4Rat0gRg*n?)~jDMT3rHb8jNhn z)oB+kBLh7<34M}K1;ju~UrxGN2F`|qHv%$($06O4HL$+09UUwF$ETCST|-H6PG_(9 zmpu-X9T}O>*-Y3wd;sNb#TA22H%Skro0)!6}#?v&&J7AY9lIcxKf7S zSE)jZ<|639=gio zat8_}JMTEqVaBu|!bv9-!%kpq7UB)7w3VV{M^N>#SiXgQ!e)e{3*iFug#PJY}kUVhOdc5$e^T<@AYx&+tdH(*+`e7jl@5u1IJ|$M^#)7lHl>w&RY)TVeG|#x9s>AK&3j8u)C;&r-Ay{yAIBY zR}?%Ddk(!2ZX%e7e*!(^8WgS!)QrZpoGeeF6x<6W0us^cu}an_ZF9k1jUdR>hrQd*FvLUKi z5Wn*Hn_j^*;7H&gRd$bG&p?8wzu2Y<19>!PJU<}lgFa#t^wgWF_1BR!I$O7& z$IoF&mFJm3Hl;RwTqFSJFfNV*O9=8(!l*cvDKVk{Q8$RMih_FT)lC7Y`~-UBmeaZQXe2(b-cWf+c z@a?bISk6o>aa+xkye1A^|IgLvbq|(K9CR`Mm5`&ZS7xSXn%a}IA|Rys3W$o8i_`m} zA^37&O#6H@hvS7N=S|1@C&JK8CxcR-U1vO?2`|kIx7}w9&+d71>-Q%RrJ_Lz*@1~$ zBr|uOFRB8w7_UTP)jAc;=e;}eo98X3e&drsV}7GG2!(eYg0dvY!;DM9_D-pWDiIj-5b zU<`D4ySX-l00(`W`nG(eh)%q3W(`|Ude~~5>ztf24U`&!J#q37%AbJw8iv^KN9=rO zpmm-<|1JR0b|}lYdEn3BRJC5oaxN?ZRVualLA&~!7i;LdVh5VNS4(P_G|bw8 z9u835|ATX<#(%_=&|_G6EE<8to3*N+Xwu$x8ph(^W8fm?*`qR^vl_t^B$%DE5*EGB z6K+|W4w-%@1wyS*IfNL)<&Xc1Yk$hQIIfxRK>$!*SbJ~tF`SkQXxP-S`9D!{Ir3KvK=CKj zy`N&Avr&;nc4Cx-vNQKV5xmR@WA0yzE5EmeTi=Yh^N{w2QZchmtVk2^lo3<{pdjD7 zc}2P5x|YvXi~a|kKIIHfPWWhhIy^LZUS$;$hiIG_PyT5N zv6`RU;CcXX<4+M`OfgbT3;|^8<36m2g)?PwZ=-U`fnE$Ryz~t5ll0sOq>yhyI&2Dl zeR8%rY{+M10YL`658}eGDh=06GTt`bky)$ptyjbN{gzpBZ8goQ4|Qhxy$ie&NnAjY&;umT3$9qIBy^0RT0(DzZ^E z_ET5Cyq)JCI_}W|pB!iy{WOgSwL5U}*avMk&l%$BhKn1XD!_?b-c1wU->v7E$S}XX zyCl4QetAro_Oku<_glx zj6uuyrvVc$q*{JyW{S4kvOJ_9*fu7pUw`@OTXRcy@cq?~vC zj|l>Hkg<*~Whh~=@dKAp(Ieiqac~Hn*fhbHpOeQ&#=72zqHIgltA-z||E1gpUMde( zJ%rg;8y_uM$C*7CWy~x&a=?U=N^UY>Cluwx+Y>wTUkLhEhsCNSA!aiQq*?A~z9GTJ z7(-QASCV*?l9a=z9;7`eHr3XWt(Jfd%XmTnG)qTJFmrWrn|nd_(*tu)9`kQZoyR0j z73>Y0UP?XCec`F$zo0YHP;u(VJiK$w1sBi&zJ$cczOAx|b}~=pxc*>2)u1&ksWxFK zgr-s)4aBd_zNzKdn`qNPmRGO-q0h7C|Ip_uBZZGA$mrxPZE13-bNgOcvI*F3Kc6xd zD7H>%ZWSi<4fu3?o$6I)3uePGicTc{_$z-h>8)X0{nEzRZi`YjDP+GNu@K9I!fW@VdoB9{K2_^AK`YOt6fPF2q!2+!E>~FBmisDWN_& zA6mn?*Eh~{!9m))0~FTNojXxJ;rsv@0`)uPNQ;Fgq#weU~qBs z;0M+${Y{{o(s9p+a4}X9BlE#*SsH}E`B;UDn-CeIweWWc$h}rlmKTsN)|aa=$BKD` z9M{6>1-`-o0$ppcPrG36GDAzn{#M`9SU`u-a`SlGm;0(}c5KKDIzP)jt$@aTAFVUQ zHwg4PSYv+8_gG|sw3!yQQy_PjnKs^$02RnTMwMOb?Kq7<)-%ZNN$7(y(4HsLN%jB^%1t8 z4)k|T=JW2?sPaQRXStduKjIjW8?R#WLX@nE)pAY9<&bETML=gxASkq_uk*JS6nE{T zKu`bd2i80;636G++C;J&SPFTsD{rRhVefGSd+{{Ecc!jftrZUAO#n7lwKfqMzS5e* zt1$h`f;=fr1-bhS8vLeqeRxt_S}@G{>gsvfEEPO5(z>=ss3=z8gB%{Fq|fYF}Z|Y?)()5sXlL?(k{4|(jsbEF)Hwbfpd&8 z^Yuw6MF;R+4)uCVj|&kli$Dh4AO}@OIA_x@a0x+|*=wJx2qBX@iUt?Z$9>wb5Z)B5 zKwXm_-p+!U8j&MMNU)*~CA*Du462%%{1_+Wf@T*rG3@@C$;a0CPjl2C5T0OW1rd5i ziseLxRl&uAVEFPQBdFeWdgz4$5#1&<5&zNhoO7e5nh7%FMhl>|Ef5Blh>-D6CCbCX z6~fhN+-k*4l$oy#h~H`%tf|N0ZLnTY)0by>%imX(B|p-*dHo8=C?0EPRgmwrM$f0- zKF#vmefXV<+sW8Ky0uz=tM!#&?yZNK@szPw_4vcW;=|lY50Qu_>WQ(f7kj2PN;lAA zBEJUeoklY)Ql&KQ{X~)ZnO8QW;C1LNX)ybN9R{Fm!GIsTIdl=s2v(TM%)259fbR(7 znbCon?4?*%2!3Ju`77k#C%_CH;R^U1!jZdIoM?!d_)Gyfw6gr$tm6oQ0g|QN_;q{y z_h?qgrB7uX3im75R)QH-f|;+vC|syLkYha%3Rs3&LJF~d8L^7$wD=`Wv+jkE0u}IX z_3KzX0op+E3^C`dFbDOrM68GI=_9VHF(IF^pFmzbftZp6ZHz+YW}XD4ir9>_8SL5O z;B!AvXItVUigLqnvW$>?jcD8L{Oc+)BSSod{yEHZdIlc=y75sQEfxZhuz;!`@Uh^M zaYym2G^}CIoHh>vtseFyQdc@?PV87t+ZjQrr%Vr>D6uL8(GXG(787ag5IVYol(S*< zyAcmy-3H!)S;)crONE&LLlCKm4>RgmIdHpHAkkQ~tr6@h^pE0?+f(sDJR5&AsT!WT-d z_TI71T75vTc(yVV8M>F%=}KZXRJRGR@U;h=357}>7G6HOz;$bdh|ojOwK(^({8w3| zd8H;q?$~|wI(j^L6$Z*#so93uub#w#?E>FCbNs*nzHgCVuQ8}BH*S_0KJWZRux#Z} zKXr4rbVH951@zkApO`nZ+K;z084gq@~(c}4S8=6}^x$bQT2OUgK|)bEsS zkhG=|5zK(*!)%I_1R!!yhqD#4n193ZNWCZoT1}d`GL~DA`)ja#r>6UR#3V*;9}6$o zL}Wf96_U?)v%YYG(E4JB^aP%?JWJAn0Ok;wzEVker;>D9BS$@T zunotnuJ^Z^Hx@@%f138)p24l+;*_A;trDc1uoNBzO=JDWpG=DQ!3E%1o0NGsE+rgp$(z z53=5reCxnhJ~gBH<6!>W2e_8Y6slb>7AGDB@4t+wb=%pU=IUpBzH&}ck(t2Z(|X!$ z7q5I_mXRALA#vvow`t~FKG!-@08_fFasU6yfA*J8``wi;S6SX<{t+|-#TMg@e-vA? zjinqWxHTr|1PaTntfsTlKiQ7xT@3iQWLOj@DO-=!_Lcw6c6O2^kZ5C`I46C;gRM#0 zxNeb%I9|ZRq+b5$c12qHgYCw02%_X*(0lEX+0D!Gi6EfV zf}p3K!d6}tO0lLonWVpU-ALR2%=qEthg|}fN4=qSU-_U}%TdPe$3@}FkUfwOLuroe zPP|*$AOiuQBlS8QQX#`0+4ZUbB_17wk-x;T8<|qNGFvO$7)n_7wKxGwt}m`lj}wI- z`)}>9+ugq&ym_|_NF@qj@%XtkO(h~*fC%NgBA81T?H>h~763T^TRzmpyzyR~g8PdL z8^jPibXWIC$Bkc5;W{|uEJpBQTxN=D=&KRfpXv){hrjJ*xjq1KOOE5!9ZuG?h~Yfg zYVB4B??ow818S2^%v=V`GHS@FHR5L9d?B^T_f7Y~2Hj`vJ{uBv?F4pWkvI$J-`b+S zk-xP?8yi>r5X1G21ewcstG!m)jXO|@o&|#S(TwBB;#k&K)CXT>Sd)Y69p{g2hH!OC zzIoMg@Y%V4%n3gyea7>%WM-`WeyRCW(P_eC$o0wFqnKs}&g!VmR!>XXU^0DbNKp?N zqKL5LKPxwi`Y|`|p;X*!wJRDZr;~?Xp0=frb8;kH7Ybrv)&92hXk4tUJi=asg?>Xj z1DnF)bm+UY#V|Ozy9MMa4nN4+oA2}Lg~wads+()#cr6goLNF05 za2lloB5S`8HLlMxQ$`F0a47OREjZgx?nRpVxQyDU{bRfD z(fH&~gSuE>#D~V&YMz|-pK*re<`ee6q0Qbg;!rGmHNCuN>JcKuv%y@$wjwT}2bGg+d+Anjjg z%M+*ZjEa`-B4CkF+fXf+;`YAr&( z8*~N;lZJdb$bX;H?)0|c)_HpW$U}fa&w}EM&i=wBc>&i5i!2UXNh>YZS-x*%EGJ@e<@WLXJzQN6m?IM+zR(Q!t~`I>D)@? zjIlBBpbac@a$p7YOdzqymN4w?KNSDpcBbt<=$Q)bgK`G^M()*pj1wXue4S_Yig2Yx z5^#;S6%p2+$Q$qw)Mza{wCD39cF~7TS!#6q&g5DYt9)v&noNk?=h@XWYdAAF6qOE_e9%lu znq|<^|3(jNZQRkTR2xMTOCKI!oEDdEK|pf-QdU zm)j;cTAO^ZWB`-faL{YIJyo0e4Z!$lg@0iDbzR_ilx|e;`ae|niL6yj<-OwM@=v5B zK}4e|fV&vw4=pPen856}*P-o(B*&8UlGgORgQtSvk18L_c2og~eN7%rbL)=Vmm@u_^vGy#HDKcc&(2{?9^P-K2bu zu2-Plt5<}A9EU9N8c}N@+$jz0Z2i0^BK6Aqt66mZwWv~_XJX*o_Q#iYf4Fx`Yd2yu znrYHJkSL%1N_e2*4%3MUd?w4)*&)-*3^HHyx9d7`-Cqy%PuKNM1h&z0u3K;$xuMC< zp7;Z#C4&!b%p)HshHS#_0#oWPn?F9RrFG_Lw3!|mT)05dmzI4yTbQ@eW;jpE`-d!9 z4Q#kWe%o=*5()t+o?0J~?`K_cV)EzjRMUi^!qj8U_TW1JTbJ1euyxS_V|mB<-DGz$ zC=#522D5CN_&&^?ZJ$ng7Kq(U;#&0?6{OUWhJRefZ^Y0IJ=48Yd5 z$u0twOJjd2>}!TTBRQM)$3&?P^nvbmK^G|pCu-1!JUOPkj07NQM_MXB&i4igM6*EC z7LTEiiSUDjnN<;DeB&YPIfyl1;!vfEWfF+`Z1UgB@GX+W-fMdgW8g*9@q$tJ%CWeY zm-hOB1;AQ)sJOixh_Id1?7@9F+>v#VA$uV;bwEXkx&3if$`TV_o5hR1lK#iN zh3nl5sP*3RoJa2BS?(iY$#*BI`<~m;(Gzy5nrJc0l1z{`g>M26yq57+7cA?#hAf!R z00o%|&nu=ByQS)K8}yf3h{@Ixqdz*w|4{Dy(OGj3QXLyd;%9h`t0!)Dhv|3!(&n*n zoS%7Dh%NzdGq!CoH>#^+H@hR;$n=p7K4z3#TKp7wmufxsHZZ93?yMpDhA(aG-Rr*e~Y~wIF;xeIfp+dco z&vlq)&T>W(Xh>*BPE~x~dMRhW$0!XE%+2uqc^J%U z4P@T8louS*pmBA={!2vv+pe^u^{SlTHP(44>`7f#mx-589bC^@Pg|Rt=bom{b?58< zRMF5A{HKa$@MGfBLg9!vh95Uh8kI5u^HZGUWhh=?ryPBMj>AH643=_z*u=3{8VJr) z9;aHAaenuNn?B=z2xVA}{w5yS`W4^huNy+*e%pdYNEx_xQ@52c`a0ea8~Ylz!q~cCLrUGzit0 z_;DI1RQSV}jr9F8=_{Wea|Z)si+l``;a2riTKtMf9!785 zj#(ifBg-nnfICsC&%#tu#J>$&j5`KKg!~6+y~&iV4Q)|?kwSYgP8 zAsVp3!hrYUwR)Z;0-&y~9Ygd0>KaKP+6r0>)^aL99`d-1{@;2PLh*;j-BcB^jq?>a z+`F|)g)Tk;!xCTfhZv_V%aweXroM5If-{f*GE^IiM`o2+d@i~b`f zns%q^(>ISodSFWj1k=GBu&-ZUH&6&J>>HnA%hO@c{#Leh&^5almdsXRM?!S)a#X!G z^8L62B-&wYBYnpeTHv>1CpHv%gPendfZP)P3ljx)@FB%)xp-g--&9d1ovGqrF;3#E z>C2P@#%Z)V`;vvCV1#Df_}gc;4uWRgesEOU!P^c;+O${dAEJZEA7BP_TWL)Va&z*u zj0W3&8a8Qr|GS)o&p+JhkCb*B6QZLX3N0{aSNv=C|HVpUMZcf9Q`B-_#b;X%JnE5c zd|h>Oc&;gXdm83e%wQ_{x0qU>yyxlAvMf5wtZ+XC$5$DW7l32%) z(gz*=@&rbImbw#G-(0(&6s_U@866)JSSLEP_i2i3B|ZMeK2Mn{A} zFcarE=g{y-;=ZZoaR}n#lK+_4d76%~Hh1*wE`I66ZS4GPtA6X-Dm@ml_eZXrtu2o+ z=`HC4B&SkUpS<8heor{xvl;WLW^I=8mk(!y7GfG?OI0|>%=2%x9|wWTzol#RX}#?^ zgQU)6iTqI7u2Cf@Ux{1acJJrUEG1~TLV16ATN9xgjER5Vj$76@D1#SjtL=I1k1j(- z^Q2pZ>71o@H59J`%~tvOG=bG5zCAk=t3;LG9zqrAAVv^P_i->1Pcd zkEBZ{ezS#sqn#z1n+`@}7D8NGTQI7e+y-%K0mCq0p@y}kUSv8fZB+c3zufxOp1Wq) zq%~n=h)wXMquDnsP!qKP)PbLxJ4y4~ne8`~4J89q1Fd8vrw=pa5gr0<)*1mN9AJ1d zsm$4{N`!5o226td@Qg%qa&_Qwbf{MU4oMf$@w*e?3iaa2MHyl#kt6^y*t$VYaX33v z%?|XLz(2i{fUGvM$!3P3RTvZ}B^ za~*;lps*4$GhXbR!}?fNKCq#c{tCtov#yx_CjEFTq2E1xuniEw}>*V&=IK>dd$%9^xWu z;urs)$zgbKhbS(dCpSj3p0Ws16-h!6oo;}sLhtY1$cMF-?i zYT8aQ1f=#$&!5SYoQSH&jT3>B3?+Vu1>ORacYXZ*^kfDZyMo2NUpJtSJMcWjMV}X! z77M^p@L%G5?-=-WG5+4@JxDMmqjpv3uN9E4p)l!$SMmFy^F;gs%CG+`nlrE;Ffzr_<%gL2nTXHkBBB@9*?*_3v3TCWY5qE z@xpwaj#;I=_|qe!TrBpJfwb8{$2_y^&u5R`GxY3wZN9^}cstME!LVhlZ?n?k#_bGE zVff_66j$>Vj?Nh&%bL;*O(=~Uez$zkq@_qvqKuhimqgk@lQSjc{;--Pr*7O~bKC68 zwblc^pWixoykoxye+Z>#4Qs>@lbM{|BJNTwI+(SV6vf0>OL6U&VtgL)AU(F-O9RKHl*`_@n}o6PdP=kZ|JV9jGy!%{^T%R z>O$~s$?^f?aWZ37-tlw`$#tZV*?~*p>_%e~qK@4B-Amc;HfydoptuXr^~CQ`mDdi; zRrllsn`659?#V)nlhQQv4G$jK+VPFixMAMi*<@ym2+>`(97W$X+=vEp%6bb|w>O2Y zH6~@!&>TIm=t&F~?Q@>ed(4s&PqbN0#i*S?^_^JA+q>bcw{=A5_08|!ye?yQ-<|MF zn=F#Yq?a~KEbhH&^t^FY0}?(QY~8HyDTolQ9Ju(YQ&B87-y+K);m3C1+#t;+Ou~6xIx&vz}#d|L}3Kz#` z-nH@xqY_%$7TOYGA?ZTN0WPG3eziN`u4-|!?;WPU3`Rs9g&)dZ%n09$*$p0xu-?K_b6%kTC6HZo; z?UfOPIh4Hna{t^P+PFH;p=O}Cw7hlZzj!rdCgg(U!NqyAv8yFciGv|@5tG>&z!j3J z@TP9eh?VVVv*fG8lInsE4{(v-Fl6h=K0}ZYAl)oA0}p!h59Hens#W<1ZA8M zKin32`hJk5N*`Ef+$yGE9nzh1ht8XgTEBb4n0ZvHU$XP&)=Q&xr@QZq#oV(8fLqd#jt%Tx4L&%Iu(~zU-0LQtsvrO4gaD)}H&)HM%L~ zIPLHub~pTS(d{oJT@uE98q`QP!*O@4ip9ICUh9XRZ`QrtRd>078Je2_8D*+8OIR-B3MX~$@Eo%#WPGbTPc=CXzQ zGHjCiSbKs(DioPA;i6V#|JpO~!GLe)+$UB8u2xZ9Cn6Pq2*QoBORbvDdS2Twn4d58 zWR26Fj%GBNg6g)>FTvO&qivaRnImN(KW1TngVWkkE!tl{S`~p}bRlQVxnR1C_Ta$w zhwV2b_S$@V%DOS!B0&|X17pLQHdtfUpVgROpec)G*WfWHasrK?3_)6$Z%xT!5rvo4 zq7#zZSoj!8b5|&wVC;59iH<)rnuMIFbJ(aPo-s7r*J0zq?g&F!>R;;=zcH&V8%rDp}-mUlp`z3~QXjWwo2QP^Y1&iaU}9I%na%}Kzc>@I=^F5e@OkfCcqbV+|o zw8_bKS&X(M_AZU;8ry`mPsj(Y$k%U?Axm)8!K~_663>m*SpjnPtdyF<0`=X{1F6;DO%hF) zkiZAdV9uPua4sm_-EdOe<}fl(m=gy9$eni;D|5 z*OqF0dw(?Z;Dr#wj|<70pGfn0-{GFnYqtHe+7oiD>jA)LDKxNoo|(DazPZL6EBoR= zyNi>EsL3jjFmvEw;vg4&|64uln2mfxaVLYnrkw@v#cT8KlqRBiXPPO==<)zGUhhly zr#F&Y(`>PHJeu+vsfMtd+mwCL!_b?(PM_O()*<{@73&v%&qD=eabrS%9zl^UJYQWZ z759Y5ev_)nB3=5*X`_fr!whpW+>6d3NZe0KSzWSq(*?@FD|-Mg$-5z6F7HfnX+&fg zT_({^hW_IZ!9|#s4;wnkck2cCc14q;Hm@>%SD0uPRs^LIW{lJ`(o=l@seQ|Nqsx4v z-1sSV2SqMhly8(?F-v3)ft;Q_~tG>-MwL ju8H#df%YQjJvNsAYyat>s!QM>(ovo(X((37Lqq-_-7S*o diff --git a/tests/visual_tests/images/marker_line_placement_on_points-500-100-1.0-cairo-reference.png b/tests/visual_tests/images/marker_line_placement_on_points-500-100-1.0-cairo-reference.png index 8bb0b76497b6dcd0daf21a4212baf14f0d129302..8943bfa53a3292b11f4d0fb76dd143d6e78f5cad 100644 GIT binary patch delta 731 zcmV<10wn$72igaaBmtk1C5?Z>|Ln=*@#E<8=>PNF|Muno`R)Jz|9N9aF8}}n_DMuR zRCt{2+*?!HKp4jHgp^tYl!Ks9)KpM}wUD}e|IgMvP|eV*PT4oJ`#k>}4mWSc-((Gt zBs;{MQmV|dEN{7T^RzT?#WzE^tmzH-uPUXsmLbbc?s2PB72gcuqDN&{rIYaGb|*YP zdhi*-#f@&)UR4%gbb~9op&+hJMhyH`C@} zG932%!<*@Yefb7v4vM~hS}f-C#p7yC^yG8P`{wbk*3*W|d)nCpxz((0em9%1c_5c^ zc1K^&{%XUIYzb%47e;yh{UeLMLR;U7PV}_n^6rB|=YgCL3!NX?Tv+J*OLE4AiO#QK zf#MG?E-ZBZwCaLF=SdVkICP#aMRcO49#4VxL80?v2OkzXFKcpPq4NSp7Z^G(Rkop_ z^QHs`M0DPM1QOlG8x1k6lO6*!e~jpWC4#Q2D){-#5;J5<0A26Zv3)QUi5YT5kN(LK zjznUHWU-@bby5Hki5a3rj_y6m@pVyLkIOJ35;J6r9Nlpg*|EZr#5hB?xX~-~_rK3- z-J3V6_+|(fJ^D7c5V^@cZbP64pCMe_=yu_?3@w)>DbK$4O3V=MH($6c5$3{7eS`o2 N002ovPDHLkV1mN@dAR@p delta 741 zcmV4CVYrFJ&28 z30EwOu=H<+a(<)B-kWF1ACE*l&c5?%%+SqqbXkUmWPI6bU5+is`4^$a4Bb3OKZ;bU z!9!z)YQCc%%A(3q)Z(ErLo@Hut0cwV=NWnhfG(A$5N7BU2s-ZGLm{H?-(Ej@Lxjgee}6scz3C%T4-I{PN-m+Tb})2jzFSO2qrqU5-Yj;A>R9O3 z{5kDcnmpsFW1)-hP5-2YoX<6VWU4`-%R>L@q`TdVi*C1PpZ_v%H+^WT!J!XC{M(mf z-@E2zinhNKo#eL5WW`@`$Wy?M2L0W$|hUp>y}v)TM%xgvV-+3kJ(a9ind&Fwwxct(xCX zXDgn_g`6z()%35{{Kyt?f<8COvmYM``Vwt_Cpyu?j@!En3Y{l%E-Z9@QDn1Wq4Ucb z8zwrxh6Rd0xY)4J`O~Tm3Y`~GxZu!vxfIcf9(ue4+69Hqn;l$O=)A4ThK0@>7;Rwa zyj593L+76o91zj@_al(#68~t3VMUXn0W6b10}6jE5cJX%!VH}PKrf5@SW(-B>>4xl z@*e$@!;;jp7ByyQ<~w?spHzd0#thXwM|U3C_$o=R##ucg8Z&hB9Nlsx(Xqsm_&7s1 zztLq8-bIz}&c6&bW+>-3x;eqx!~zkU?Bm9qX!$ooIls|m;}wRMO-Qn{@4Xr`l=}@M X&gl^~sLd0!00000NkvXXu0mjfo)3lC diff --git a/tests/visual_tests/images/marker_line_placement_on_points-500-100-2.0-cairo-reference.png b/tests/visual_tests/images/marker_line_placement_on_points-500-100-2.0-cairo-reference.png index 57e41b1f50b585fd762db5697b110e393404e0eb..4cf7ae33ba810eed8aa9a743d924b019f6052cd7 100644 GIT binary patch delta 1040 zcmV+r1n>LI3BC!CBms?)C4YbG_3Quh-2e6B|M%(t`0M}w|Da81sQ>^2>`6pHRCt{2 z-04!{P!NXU5CK(w6@}Mqt31G zdi`9z=&$GTgHCw{Ul#WP{JvH6T#v0-#NKn*v4=H|9g3y@+4Y0}ZdeBbxN<>-0g z4aqa;G5}rsZId4Y8&By}C-kR7cb|C0)dELKh z`5hi)se_=;tH8_f@oC-kPhZf`%j8-1Cyf1F(X;3aMK__}ZSU_Guhc=%KYnx}cc&L} zMx-yDq6(LD*p4C(}nVXZKy_c5-ty9A4c_Zk^1ZTkpOUGkB7TzJJ=6lcAMqI9YZj zLNS9Wk?83fTz`&itB^ffb|OMCgDZ*Xe)5bz^okt2dJoC{EnZbo%wS70x>H($@j`UE zLqFyWwj`oAcw|IQdj?yA(Fb)NmNn=K{irk85{Mq}yw3jl@7Z?0Z`+x(uhVkxrI^8& zK=k;!dYo$X(^8Xh%WG?j8GH#ukGIwH*rT5-7P)A-_Zd>m;OjpuH}UC21ahYU0000< KMNUMnLSTZ8Oc6Q& delta 1037 zcmV+o1oHd73CszQBmt9=C4YbD^XTgI>i_fH?)L8g_2d8d=>PcZ|NsBWbU=6j00Z<% zL_t(|obB9wQ>su9$8jX>S-n@wN~>!np+xHYe|1;n89{5nUHAO<`=>gyjGs9NB+Xij z%L%)%wzb;1TH&J?(Th%b245EU0sKBUe6M>}Bx0u=cI098!q>Q`JcEBPf#_cNW>2i7 zmc2o-6OB!i@HO5k&)`cSx*u(;9jmfV^b-G+XYeHuJwD;SREl(7YM*W?X0Rm@eG!YC zm3y4U9*fhS!Inhyc<-$cNj&Q8yc9Fol87E(TRYCQYZp1}?lgY3)^#<-46a0?ry`Cs zyv%Z34%269dQi+@N+y4LR>YYMdpB3MeRb0tPMiikC}!{^6g?+WS_2*wGgy*}en_Md z4~iKaiACQp(vSzm42I;Qul-6Ax3SMr6f@`(jJ{59q2y}#b_vA{;$)+jkQ{-7Vg_l_ z(Kq?NH{a~S?%eZ_w&haHpvwSs?YAj9V2Tcyq64PrfGIj)iVl+;0ug^HI$(+pn4$v) zh`w4am*1PdzP`Sfs}=X*Fa(CaS_am|zQN>?&`ZIimfQzN84miYS{!Y;qo5xm--g1D zf&L@%V6}4}C`}OPd(P#M>Imos^Ut^E@zcZTelQq4y}o^X;%)H&=&Ky~c<`s#_ybJc zeDsX@Y_tO&HhBF+Qa67eJzeEb73A9<5_a>?>&BxeC-moicb|C2QdELKB`5hi)se_=8%D{KwXBd}qXz07-S@vg) z{aw+s=wn4Uq2F!qAG*DpP=r4F(S_W-UdS1deq<}jboAur%-&Udu;KE52U+OGqw8(h zl8i^!Ke+5>KDz#Cbtm)D^-B~x0if&GrHUb->lY&nL7?kbpz~p%>yI6Bp`h!}nlj;_ z>kk-{kkHrqQ{^x)^dSAG1c2z8lLrDOe-Vg`M3(JMK&BywgjBo=)iN05{m97#n#RzJJPD)IAHLtBHX7D8tJ=#_;BO;G>u1Msv<_VW#24DXHl9pbs`Y}c600000NkvXX Hu0mjftP&T( diff --git a/tests/visual_tests/images/shieldsymbolizer-1-498-100-2.0-cairo-reference.png b/tests/visual_tests/images/shieldsymbolizer-1-498-100-2.0-cairo-reference.png index 6a0c1a0fab3215d17dd16259712f61fced6f2dd3..3bd5dd3a80f858506b242b629591b52a9cd9d81f 100644 GIT binary patch delta 3087 zcmZWrdpy%^AO3ZE-g1mWPBF?*kwey)99B+Y#VR?U9?D^gGIQ8O8FJWCiODK+K32p` zhQdlHGR!3HM@b_SiHE)Oynnv;{m1>euj_kV_xE%EaecnYitGDG2exFB4{EDQNJs!$ z0H6&3dVqog02l(Yva$di0L%fvj(kPY0tg3yC_oFU1zZOJ901?}AQ1p!B^2BhfapC+ zQ9z6m0EB7-!3JWt#EgOfz}G^GCg&hE#CX?wV~IGSg++F*Pxy=QGAvezLSY$L(j5}M6)*JhY0Sk&-e6OyRAEHK zA{Lv~okcAcZP6+tu}Eb_R3R2iWl&#=3h;PpKehZVb-bWJn3dJiPW_Re-``06La3%L z(>5K1RH}U4Nksqa zE;3ZE_}2@$K9`xdkk;{vQtYxc6M-`{i|7x$GwM8mSzNbLUjy?3Pw=1T1>doqe=-Qk z362AZ4#AZ}QIkwPbmfrDA|d$-%jtW@SJi}u#oEEb-`pLGA|&(s&9CbRSJn;{etqfh zCm}^Y`_SwQE_k6

VNf(4^}#iE?fqM;HRFG6TCht=GhVmR8rzk(6_H@@qHVxVT|i zz2Znlg9!52eKR`FZyeI7zLTH8?r270kcaEX#Al`Ga;gpc*s6p`<%+fl+z@9@hLE6y54n zhbhbD5|%}f)Ky~V>HRb6Ai79DjFqP!Kd4UfFg;kOKlGf{sZeZ*nYr86itg7?DBZrB z-kV^%=KS)u3iZbE$k5X5%f+z@@RaB*?w9C+gtGlMeT{dvMk=razW)vG=G3kh$)k4t zIK5;Txs%_opS8cMZiAzotIzynx_(h!a8`Jpe0qqm{zqf{t*kg-|H8SB)wqpc-k$TC z1nbT)Hg)J4Vt%;c$HdB7FJ<1l#9}rad|SFbR`L%8hMCy-8NT}blTA&$LmR}RjTQ07 zz`UQtR11IPI!9EZvz)}iSo+NKp~>G!Fj~O53SO9O{3k*d^l#pZn}mqNw&F^Z{j^BX zcLWy4>d9I4Y*^Qk0E{Us?JCNCxEti<6b1Z1i|@6D(~`rs+h3k-WNsgc`cWmFLJHls@Q zW@akl!-KkvX2LF#E7D%Gn`>-a^N_->#Jidb(!Z*DsNAEmt zbb)a*Pz6UdL;tg2lZ6<4P@mB>gA_^2mi2&SuuNeGfN;;v^4%570#f)8|+#xUWX^Dv7Lpt$@ zX%v={ZVTIk!?cBQsKvdaU;5TmhHH8!q7>2{2 zn|awf=b3c&rK+l0W2ND}wO1CtNO)T}bldp|-=le-R$X|p2VwPU9tA0oY@hDaEk-q_ zWxijGI(kpez0)5W>Gi@@n%L!u8jZ;|OWXfU7s-3idqv4XZu@x54veCjP=xUVVS3t& zZm~_BOP_Oao!?=V9v<0*m=;1qLq%-b0%L?X*&VVBec3)$T@AGeiwJ)=RUn&){>NAX zhuKzom0)un-Trz$tB5km&8x*cqpUTJ798kJWx((Xv4~41p>J;C*a?O7{#XR(GTyzY z#!AD7gK2P|c;QZo8fdrux=?E}oZLSxx5x?QFV8cWjbot|xlPYL5HLdQZwvnXN4(ug zL^R)zvCbT)j*Y1uy19(sCm1ks<~?414XY^+`ZdgniOMx1-ucIA#0UR8NDcVxr3VSyxpMSGqk3f+cz$DPpp_ zD3pcgh0E<)eQ^ka`@K!0tmuuHt~l;aO2_XIRj`Om11&aQu9?p+jKQeHa7CTprufyV zT85_&;XW%gM+Xb+*0|t^n}HCAR+o0;zTa(&3)C%iDww78%el^}b!rv%eY<^kEsQo( z>D5B_ImLO><5s0=tcG8CGyJ4Gm%M-JcA74d6hCu`@Kw4m9y+i$h-*X=ZnZeT+>6}b z6FL}iapd>=$VAVX(bUdc!$>gwDkKR}d|cqm^3p&B|FQ>TqKrtlIaXJ}5zQM2@4vOv zTJB{dbT_=bjq57WOuq@FxP=(#prbFZ@Ykl=Dg{x}M`DQo2)RyR|D?b*>-L;5otUaL zB%Ic*i+H;&4t<(Z%|=;{#q(pH7X@1cLB%bDH@LV*TkqQ8u@vF9(DSML0W} z!2TdPG^N%k+rQ*rBRO|MbmOO6W9&j%1b&PzUMYn;gjB*Bk5cCB%8dm4ar?N z=@`4pX}7=fZ|pY{VP47BCk&6GDeg?zIc23Y=Hg@aorm}5pqXj$#R*52xs$YCU(9 z@aCMOdte1lfBWDqmAKp;acz>|NEA0wF)-iW=vnk`Z5Wp zclDG}23G`kRUDMyDH2?{B9Hdzz`zTR7$3gUPzXHCY4Y0D3yuVZ9dYj^=hDfdXpX=2 zVDouD&AD`YW39tQm?STQk7rz1r&yL?{!=M-)kE%S{6s+hNGd@k+`@Z3uDT!$ z)*(TLZYJFm88TFfci#ZEB{D?Y9<*ZZ9#d`bSDm;eTQVkC{XSgwX^pegeyuaQ`^jVT zE1ECNg6M=@?_S&7Xi~grrhRGem?|&Rpr?t_7j5hRuYRY=u~~GTUxH6Ypj*_c?o@>a z&i0HhvAU5#Q8A=q>dNW9?S*Ylb>;9^PQLPCAz7OlnAGL#8dYTwq=?;v32~P0{PbZ* zvFAAzoGEeqh`^G#`Yq|q4C|uXyidVU#aV)vKR=e(5)b~oXY`{#Bmq}3RVV%iqOV3b z-gWBs*SguMk2Ukhz*ADnxrZ;yrpr&)&B})ktEtQB=$xsc*_TO>%Y&c^qgHxO)*m!n zd!2U#?)X>fc+5Y54Kx6mk{zFrqB4Q zOVcRiD;gk)a<{%FTL%tm9ju-Wa1aeM_Oq+|2HLIhpNlV8l&iqOpiRO?ite{bkdYX- zf#wr7BHjI}nrs<3*j4OiQEt3>{v+I>Z7)T~@z-}r9D#%K*FO4-#D7-BEcWNE*iZ7`%}&*Ypi{f{tFBn B3&sEd delta 3162 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 SHuJqWIB#xyuGY-^_WuAZQwH1s diff --git a/tests/visual_tests/images/shieldsymbolizer-1-501-100-2.0-cairo-reference.png b/tests/visual_tests/images/shieldsymbolizer-1-501-100-2.0-cairo-reference.png index ccc333e4ebe146ea54be315274347077296b371b..f6bd195bbd4a7244aa40b7d13ecf6a83131dba61 100644 GIT binary patch delta 3026 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 delta 3040 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 delta 3084 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 diff --git a/tests/visual_tests/images/whole-centroid-600-400-1.0-cairo-reference.png b/tests/visual_tests/images/whole-centroid-600-400-1.0-cairo-reference.png index 4dca4fb3f5b2a0871702a70bc424f739842db143..35f2d678e527ca985958fad9c94b3ff77d7947f4 100644 GIT binary patch delta 6769 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 T%0>2)00000NkvXXu0mjf7cL?W delta 6819 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 Date: Mon, 20 May 2013 22:48:00 -0700 Subject: [PATCH 052/110] overwrite expected cairo output --- ...equest-marker-text-line-expected-cairo.png | Bin 31204 -> 31181 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/cpp_tests/support/map-request-marker-text-line-expected-cairo.png b/tests/cpp_tests/support/map-request-marker-text-line-expected-cairo.png index b0960682912dfe6b24248584b19ae3e6193c2b3d..cb8e478268919e8874720ce402b822f813634d7f 100644 GIT binary patch literal 31181 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%0Dv$4HFs#A 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 Date: Tue, 21 May 2013 12:51:31 -0700 Subject: [PATCH 053/110] support for handling unicode paths on windows in sqlite, csv, and geojson plugins - refs #1177 --- plugins/input/csv/csv_datasource.cpp | 5 ++++ plugins/input/geojson/geojson_datasource.cpp | 7 ++++- plugins/input/sqlite/sqlite_datasource.cpp | 22 +++++++++++++- plugins/input/sqlite/sqlite_utils.hpp | 31 ++++++++++++++++++-- 4 files changed, 61 insertions(+), 4 deletions(-) diff --git a/plugins/input/csv/csv_datasource.cpp b/plugins/input/csv/csv_datasource.cpp index b98fdb188..403f8f9ee 100644 --- a/plugins/input/csv/csv_datasource.cpp +++ b/plugins/input/csv/csv_datasource.cpp @@ -30,6 +30,7 @@ // mapnik #include +#include #include #include #include @@ -119,7 +120,11 @@ csv_datasource::csv_datasource(parameters const& params) } else { +#if defined (_WINDOWS) + std::ifstream in(mapnik::utf8_to_utf16(filename_),std::ios_base::in | std::ios_base::binary); +#else std::ifstream in(filename_.c_str(),std::ios_base::in | std::ios_base::binary); +#endif if (!in.is_open()) throw mapnik::datasource_exception("CSV Plugin: could not open: '" + filename_ + "'"); parse_csv(in,escape_, separator_, quote_); diff --git a/plugins/input/geojson/geojson_datasource.cpp b/plugins/input/geojson/geojson_datasource.cpp index 153597cda..566cb5ee2 100644 --- a/plugins/input/geojson/geojson_datasource.cpp +++ b/plugins/input/geojson/geojson_datasource.cpp @@ -39,6 +39,7 @@ // mapnik #include +#include #include #include #include @@ -106,7 +107,11 @@ geojson_datasource::geojson_datasource(parameters const& params) typedef std::istreambuf_iterator base_iterator_type; - std::ifstream is(file_.c_str()); +#if defined (_WINDOWS) + std::ifstream is(mapnik::utf8_to_utf16(file_),std::ios_base::in | std::ios_base::binary); +#else + std::ifstream is(file_.c_str(),std::ios_base::in | std::ios_base::binary); +#endif boost::spirit::multi_pass begin = boost::spirit::make_default_multi_pass(base_iterator_type(is)); diff --git a/plugins/input/sqlite/sqlite_datasource.cpp b/plugins/input/sqlite/sqlite_datasource.cpp index cc84dd3d7..6a31d4194 100644 --- a/plugins/input/sqlite/sqlite_datasource.cpp +++ b/plugins/input/sqlite/sqlite_datasource.cpp @@ -90,7 +90,11 @@ sqlite_datasource::sqlite_datasource(parameters const& params) else dataset_name_ = *file; +#ifdef _WINDOWS + if ((dataset_name_.compare(":memory:") != 0) && (!boost::filesystem::exists(mapnik::utf8_to_utf16(dataset_name_)))) +#else if ((dataset_name_.compare(":memory:") != 0) && (!boost::filesystem::exists(dataset_name_))) +#endif { throw datasource_exception("Sqlite Plugin: " + dataset_name_ + " does not exist"); } @@ -280,7 +284,11 @@ sqlite_datasource::sqlite_datasource(parameters const& params) mapnik::progress_timer __stats2__(std::clog, "sqlite_datasource::init(use_spatial_index)"); #endif +#ifdef _WINDOWS + if (boost::filesystem::exists(mapnik::utf8_to_utf16(index_db))) +#else if (boost::filesystem::exists(index_db)) +#endif { dataset_->execute("attach database '" + index_db + "' as " + index_table_); } @@ -318,7 +326,11 @@ sqlite_datasource::sqlite_datasource(parameters const& params) { //extent_initialized_ = true; has_spatial_index_ = true; +#ifdef _WINDOWS + if (boost::filesystem::exists(mapnik::utf8_to_utf16(index_db))) +#else if (boost::filesystem::exists(index_db)) +#endif { dataset_->execute("attach database '" + index_db + "' as " + index_table_); } @@ -436,12 +448,20 @@ void sqlite_datasource::parse_attachdb(std::string const& attachdb) const // Normalize the filename and make it relative to dataset_name_ if (filename.compare(":memory:") != 0) { +#ifdef _WINDOWS + boost::filesystem::path child_path(mapnik::utf8_to_utf16(filename)); +#else boost::filesystem::path child_path(filename); +#endif // It is a relative path. Fix it. if (! child_path.has_root_directory() && ! child_path.has_root_name()) { - boost::filesystem::path absolute_path(dataset_name_); +#ifdef _WINDOWS + boost::filesystem::path absolute_path(mapnik::utf8_to_utf16(dataset_name_)); +#else + boost::filesystem::path absolute_path(dataset_name_); +#endif // support symlinks if (boost::filesystem::is_symlink(absolute_path)) diff --git a/plugins/input/sqlite/sqlite_utils.hpp b/plugins/input/sqlite/sqlite_utils.hpp index 90f0446a1..32f41d5a8 100644 --- a/plugins/input/sqlite/sqlite_utils.hpp +++ b/plugins/input/sqlite/sqlite_utils.hpp @@ -30,6 +30,7 @@ // mapnik #include +#include #include #include #include @@ -235,7 +236,11 @@ public: int flags; #endif +#ifdef _WINDOWS + bool existed = boost::filesystem::exists(mapnik::utf8_to_utf16(index_db)); +#else bool existed = boost::filesystem::exists(index_db); +#endif boost::shared_ptr ds = boost::make_shared(index_db,flags); bool one_success = false; @@ -328,8 +333,12 @@ public: { try { +#ifdef _WINDOWS + boost::filesystem::remove(mapnik::utf8_to_utf16(index_db)); +#else boost::filesystem::remove(index_db); - } +#endif + } catch (...) {}; } throw mapnik::datasource_exception(ex.what()); @@ -344,7 +353,11 @@ public: { try { +#ifdef _WINDOWS + boost::filesystem::remove(mapnik::utf8_to_utf16(index_db)); +#else boost::filesystem::remove(index_db); +#endif } catch (...) {}; } @@ -413,7 +426,13 @@ public: int flags; #endif - bool existed = boost::filesystem::exists(index_db); +#ifdef _WINDOWS + bool existed = boost::filesystem::exists(mapnik::utf8_to_utf16(index_db)); +#else + bool existed = boost::filesystem::exists(index_db);; +#endif + + boost::shared_ptr ds = boost::make_shared(index_db,flags); bool one_success = false; @@ -460,7 +479,11 @@ public: { try { +#ifdef _WINDOWS + boost::filesystem::remove(mapnik::utf8_to_utf16(index_db)); +#else boost::filesystem::remove(index_db); +#endif } catch (...) {}; } @@ -476,7 +499,11 @@ public: { try { +#ifdef _WINDOWS + boost::filesystem::remove(mapnik::utf8_to_utf16(index_db)); +#else boost::filesystem::remove(index_db); +#endif } catch (...) {}; } From 49933d7d0f1bd4b3f4f4d739a953c21bcf76329b Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 21 May 2013 11:25:00 -0700 Subject: [PATCH 054/110] remove tab --- plugins/input/shape/shapefile.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/input/shape/shapefile.hpp b/plugins/input/shape/shapefile.hpp index 9df691119..6e5631824 100644 --- a/plugins/input/shape/shapefile.hpp +++ b/plugins/input/shape/shapefile.hpp @@ -151,7 +151,7 @@ public: #ifdef SHAPE_MEMORY_MAPPED_FILE file_() #elif defined (_WINDOWS) - file_(mapnik::utf8_to_utf16(file_name), std::ios::in | std::ios::binary) + file_(mapnik::utf8_to_utf16(file_name), std::ios::in | std::ios::binary) #else file_(file_name.c_str(), std::ios::in | std::ios::binary) #endif From 8478fdc217c31b4aa8d20bffe1a47d1967330ed5 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 21 May 2013 12:55:08 -0700 Subject: [PATCH 055/110] fix boolean check to be easier to read --- plugins/input/csv/csv_datasource.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/input/csv/csv_datasource.cpp b/plugins/input/csv/csv_datasource.cpp index 403f8f9ee..e1478cd36 100644 --- a/plugins/input/csv/csv_datasource.cpp +++ b/plugins/input/csv/csv_datasource.cpp @@ -126,7 +126,9 @@ csv_datasource::csv_datasource(parameters const& params) std::ifstream in(filename_.c_str(),std::ios_base::in | std::ios_base::binary); #endif if (!in.is_open()) + { throw mapnik::datasource_exception("CSV Plugin: could not open: '" + filename_ + "'"); + } parse_csv(in,escape_, separator_, quote_); in.close(); } @@ -864,7 +866,7 @@ void csv_datasource::parse_csv(T & stream, } } } - if (!feature_count > 0) + if (feature_count < 1) { MAPNIK_LOG_ERROR(csv) << "CSV Plugin: could not parse any lines of data"; } From 337410fc984fef0d1c8d0b531dce7ca7b563e5d9 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 21 May 2013 12:56:35 -0700 Subject: [PATCH 056/110] remove tabs --- plugins/input/sqlite/sqlite_datasource.cpp | 4 ++-- plugins/input/sqlite/sqlite_utils.hpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/input/sqlite/sqlite_datasource.cpp b/plugins/input/sqlite/sqlite_datasource.cpp index 6a31d4194..fa021ebf1 100644 --- a/plugins/input/sqlite/sqlite_datasource.cpp +++ b/plugins/input/sqlite/sqlite_datasource.cpp @@ -458,9 +458,9 @@ void sqlite_datasource::parse_attachdb(std::string const& attachdb) const if (! child_path.has_root_directory() && ! child_path.has_root_name()) { #ifdef _WINDOWS - boost::filesystem::path absolute_path(mapnik::utf8_to_utf16(dataset_name_)); + boost::filesystem::path absolute_path(mapnik::utf8_to_utf16(dataset_name_)); #else - boost::filesystem::path absolute_path(dataset_name_); + boost::filesystem::path absolute_path(dataset_name_); #endif // support symlinks diff --git a/plugins/input/sqlite/sqlite_utils.hpp b/plugins/input/sqlite/sqlite_utils.hpp index 32f41d5a8..5dfae3a4f 100644 --- a/plugins/input/sqlite/sqlite_utils.hpp +++ b/plugins/input/sqlite/sqlite_utils.hpp @@ -338,7 +338,7 @@ public: #else boost::filesystem::remove(index_db); #endif - } + } catch (...) {}; } throw mapnik::datasource_exception(ex.what()); @@ -427,7 +427,7 @@ public: #endif #ifdef _WINDOWS - bool existed = boost::filesystem::exists(mapnik::utf8_to_utf16(index_db)); + bool existed = boost::filesystem::exists(mapnik::utf8_to_utf16(index_db)); #else bool existed = boost::filesystem::exists(index_db);; #endif From bd3ba156387933291de2583c4f3b8c255d859f12 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 21 May 2013 14:09:49 -0700 Subject: [PATCH 057/110] improve reporting in mapnik-config of new options at 2.0.0 --- utils/mapnik-config/mapnik-config.template.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/utils/mapnik-config/mapnik-config.template.sh b/utils/mapnik-config/mapnik-config.template.sh index 7e1237cff..a71fdf642 100755 --- a/utils/mapnik-config/mapnik-config.template.sh +++ b/utils/mapnik-config/mapnik-config.template.sh @@ -10,23 +10,23 @@ Known values for OPTION are: -h --help display this help and exit -v --version version information (MAPNIK_VERSION_STRING) - --version-number version number (MAPNIK_VERSION) + --version-number version number (MAPNIK_VERSION) (new in 2.2.0) --git-revision git hash from "git rev-list --max-count=1 HEAD" - --git-describe git decribe output (new in 2.2.x) + --git-describe git decribe output (new in 2.2.0) --fonts default fonts directory --input-plugins default input plugins directory - --defines pre-processor defines for Mapnik build (new in 2.2.x) + --defines pre-processor defines for Mapnik build (new in 2.2.0) --prefix Mapnik prefix [default $CONFIG_PREFIX] --lib-name Mapnik library name --libs library linking information --dep-libs library linking information for Mapnik dependencies --ldflags library paths (-L) information - --includes include paths (-I) for Mapnik headers (new in 2.2.x) - --dep-includes include paths (-I) for Mapnik dependencies (new in 2.2.x) - --cxxflags c++ compiler flags and pre-processor defines (new in 2.2.x) + --includes include paths (-I) for Mapnik headers (new in 2.2.0) + --dep-includes include paths (-I) for Mapnik dependencies (new in 2.2.0) + --cxxflags c++ compiler flags and pre-processor defines (new in 2.2.0) --cflags all include paths, compiler flags, and pre-processor defines (for back-compatibility) - --cxx c++ compiler used to build mapnik (new in 2.2.x) - --all-flags all compile and link flags + --cxx c++ compiler used to build mapnik (new in 2.2.0) + --all-flags all compile and link flags (new in 2.2.0) EOF exit $1 From 21fa4d2aed953284c76e070062a6c9590818db80 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 21 May 2013 14:59:35 -0700 Subject: [PATCH 058/110] tests: ensure rgb image is truly different that argb - refs #1559 --- tests/python_tests/png_encoding_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/python_tests/png_encoding_test.py b/tests/python_tests/png_encoding_test.py index fc571ba28..1cded9073 100644 --- a/tests/python_tests/png_encoding_test.py +++ b/tests/python_tests/png_encoding_test.py @@ -131,6 +131,7 @@ def test_transparency_levels_aerial(): im_in = mapnik.Image.open('./images/support/transparency/aerial_rgb.png') eq_(len(im.tostring('png')),len(im_in.tostring('png'))) eq_(len(im.tostring('png:t=0')),len(im_in.tostring('png:t=0'))) + eq_(len(im.tostring('png:t=0')) == len(im_in.tostring('png')), False) if __name__ == "__main__": From 70a216c20dbcd265597c37d77b198db687c8706e Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 21 May 2013 15:14:09 -0700 Subject: [PATCH 059/110] build out changelog for 2.2.0 - refs #1850 --- CHANGELOG.md | 108 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 95 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 327177a4b..fc8388c57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,13 +6,93 @@ Developers: Please commit along with changes. For a complete change history, see the git log. -## Future +## 2.2.0 -- Added new mapnik-config options: `git-describe`, `defines`, `includes`, `dep-includes`, and `cxxflags` (#1443) +- Removed 3 depedencies without loosing any functionality: `ltdl`, `cairomm` and `libsigc++` (#1804,#806,#1681) + +- Added 64 bit integer support in expressions, feature ids, and the grid_renderer (#1661,#1662,#1662) + +- Added the ability to disable needing various depedencies: `proj4`, `libpng`, `libtiff`, `libjpeg` + +- Added faster reprojection support between `epsg:3857` and `epsg:4326` (#1705,#1703,#1579) + +- Fixed concurrency problem when using cursors in postgis plugin (#1823) + +- Fixed postgres connection pool leaks when using `persist_connection=false` (#1764) + +- Fixed postgres connection key to respect highest value of `max_size` and `initial_size` for any layer in map (#1599) + +- Fixed blurry rendering of image and SVG icons (#1316) + +- Improved logging system (https://github.com/mapnik/mapnik/wiki/Logging) + +- Added support for reading images from in memory streams (#1805) - Added `text-halo-rasterizer` property. Set to `fast` for lower quality but faster halo rendering (#1298) +- Added support in `shape`, `sqlite`, `geojson`, and `csv` plugin for handling non-latin characters in the paths to file-based resources (#1177) + +- Fixed rendering of markers when their size is greater than the specified `spacing` value (#1487) + +- Fixed handling of alpha premultiplication in image scaling (#1489) + +- Optimized rendering when a style with no symbolizers is encountered (#1517) + +- Optimized string handling and type conversion by removing `boost::to_lower`, `boost::trim`, and `boost::lexical_cast` usage (#1687,#1687,#1633) + +- Optimized alpha preserving hextree method for quantization of png images (#1629) + +- Faster rendering of rasters by reducing memory allocation of temporary buffers (#1516) + +- Fixed raster alignment when width != height and raster is being scaled (#1748) + +- Added support for caching rasters for re-use during rendering when styling more than once per layer (#1543) + +- Improved compile speeds of the code - in some cases by up to 2x and removed need for freetype dependency when building code against mapnik (#1688, #1756) + +- Improved the scaled rendering of various map features when using `scale_factor` > 1 (#1280,#1100,#1273,#1792,#1291,#1344,#1279,#1624,#1767,#1766) + +- Added C++ api for overriding scale_denominator to enable rendering at fixed scale (#1582) + +- Added Layer `buffer-size` that can be used to override Map `buffer-size` to avoid + over-fetching of data that does not need to be buffered as much as other layers. + Map level `buffer-size` will be default if layers do not set the option. Renamed a + previously undocumented parameter by the same name that impacted clipping extent and + was not needed (clipping padding should likely be a symbolizer level option) (#1566) + +- Fixed potential file descriptor leaks in image readers when invalid images were encountered (#1783) + +- Fixed alpha handling in the `blur` and `invert` image filters (#1541) + +- Fixed error reporting in the python plugin (#1422) + +- Added the ability to run test without installing with `make test-local` + +- Reduced library binary size by adding support for `-fvisibility-inlines-hidden` and `-fvisibility=hidden` (#1826,#1832) + +- Added `mapnik::map_request` class, a special object to allow passing mutable map objects to renderer (#1737) + +- Added the ability to use `boost::hash` on `mapnik::value` types (#1729) + +- Removed obsolete `GEOS` plugin and unmaintained `kismet` plugin (#1809,#1833) + +- Added new `mapnik-config` flags: `--all-flags`, `--defines`, `--git-describe`, `--includes`, `--dep-includes`, `--cxxflags`, `--cxx`. + +- Added support for unicode strings as arguments in python bindings (#163) + +- Added DebugSymbolizer (#1366) + +- Optimized rendering overhead of using `gamma` property (#1174) + +- Fixed rendering artifacts when using `polygon-gamma` or `line-gamma` equal to 0 (#761,#1763) + +- Fixed and optimized the display of excessive precision of some float data in labels (#430,#1697) + +- Removed the `bind` option for datasources (#1654) + +- Added new mapnik-config options: `git-describe`, `defines`, `includes`, `dep-includes`, and `cxxflags` (#1443) + - Added ability to access style list from map by (name,obj) in python (#1725) - Added `is_solid` method to python mapnik.Image and mapnik.ImageView classes (#1728) @@ -21,19 +101,11 @@ For a complete change history, see the git log. - Added support for `background-image` in cairo_renderer (#1724) -- Added Layer `buffer-size` that can be used to override Map `buffer-size` to avoid - over-fetching of data that does not need to be buffered as much as other layers. - Map level `buffer-size` will be default if layers do not set the option. Renamed a - previously undocumented parameter by the same name that impacted clipping extent and - was not needed (clipping padding should likely be a symbolizer level option) (#1566) - - Fixed building symbolizer rendering to be fully sensitive to alpha (8b66128c892 / bc8ea1c5a7a) -- Added 64 bit integer support in the grid_renderer (#1662) - - `[attr]` now returns false if attr is an empty string (#1665) -- Added 64 bit integer support in expressions and feature ids (#1661,#1662) +- `[attr]!=null` now returns true if attr is not null (#1642) - Added support for DBF `Logical` type: #1614 @@ -52,8 +124,6 @@ For a complete change history, see the git log. - Fixed handling of transparency level option in Octree-based PNG encoding (#1556) -- Faster rendering of rasters by reducing memory allocation of temporary buffers (#1516) - - Added ability to pass a pre-created collision detector to the cairo renderer (#1444) - Tolerance parameter is now supported for querying datasources at a given point (#503/#1499) @@ -70,6 +140,18 @@ For a complete change history, see the git log. - Compile time flag called `PLUGIN_LINKING` to allow input datasource plugins to be statically linked with the mapnik library (#249) +- Fixed `dasharray` rendering in cairo backend (#1740) + +- Fixed handling of `opacity` in svg rendering (#1744) + +- Fixed uneven rendering of markers along lines (#1693) + +- Fixed handling (finally) of null shapes and partially corrupt shapefiles (#1630,#1621) + +- Added ability to re-use `mapnik::image_32` and `mapnik::grid` by exposing a `clear` method (#1571) + +- Added support for writing RGB (no A) png images by using the format string of `png:t=0` (#1559) + ## Mapnik 2.1.0 Released Aug 23, 2012 From 8d9e6e07cf7c143cfe2c9bb99b5dd732119091e7 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 21 May 2013 16:22:04 -0700 Subject: [PATCH 060/110] ensure xml2 is present - punts on and closes #913 --- SConstruct | 3 +++ 1 file changed, 3 insertions(+) diff --git a/SConstruct b/SConstruct index d682be1ba..0f96b0a89 100644 --- a/SConstruct +++ b/SConstruct @@ -79,6 +79,7 @@ pretty_dep_names = { 'pkg-config':'pkg-config tool | more info: http://pkg-config.freedesktop.org', 'pg_config':'pg_config program | try setting PG_CONFIG SCons option', 'xml2-config':'xml2-config program | try setting XML2_CONFIG SCons option', + 'libxml2':'libxml2 library | try setting XML2_CONFIG SCons option to point to location of xml2-config program', 'gdal-config':'gdal-config program | try setting GDAL_CONFIG SCons option', 'freetype-config':'freetype-config program | try setting FREETYPE_CONFIG SCons option', 'osm':'more info: https://github.com/mapnik/mapnik/wiki//OsmPlugin', @@ -1171,6 +1172,8 @@ if not preconfigured: # https://github.com/mapnik/mapnik/issues/913 if conf.parse_config('XML2_CONFIG',checks='--cflags'): env['HAS_LIBXML2'] = True + else: + env['MISSING_DEPS'].append('libxml2') LIBSHEADERS = [ ['z', 'zlib.h', True,'C'], From e895802289bb31485dc47652dabcbd741ba49666 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 21 May 2013 16:44:32 -0700 Subject: [PATCH 061/110] more changelog updates for 2.2 --- CHANGELOG.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc8388c57..b5eb2d367 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,14 +22,18 @@ For a complete change history, see the git log. - Fixed postgres connection key to respect highest value of `max_size` and `initial_size` for any layer in map (#1599) +- Fixed potential crash in wkb parsing when postgis returns null geometry (#1843) + - Fixed blurry rendering of image and SVG icons (#1316) - Improved logging system (https://github.com/mapnik/mapnik/wiki/Logging) - Added support for reading images from in memory streams (#1805) +- Optimized halo rendering. When halo radius is < 1 new method will be used automatically (#1781) + - Added `text-halo-rasterizer` property. Set to `fast` for lower quality but faster - halo rendering (#1298) + halo rendering (#1298) which matched new default method when radius is < 1. - Added support in `shape`, `sqlite`, `geojson`, and `csv` plugin for handling non-latin characters in the paths to file-based resources (#1177) @@ -45,12 +49,14 @@ For a complete change history, see the git log. - Faster rendering of rasters by reducing memory allocation of temporary buffers (#1516) -- Fixed raster alignment when width != height and raster is being scaled (#1748) +- Fixed raster alignment when width != height and raster is being scaled (#1748,#1622) - Added support for caching rasters for re-use during rendering when styling more than once per layer (#1543) - Improved compile speeds of the code - in some cases by up to 2x and removed need for freetype dependency when building code against mapnik (#1688, #1756) +- Removed internal rule cache on `mapnik::Map` c++ object (#1723) + - Improved the scaled rendering of various map features when using `scale_factor` > 1 (#1280,#1100,#1273,#1792,#1291,#1344,#1279,#1624,#1767,#1766) - Added C++ api for overriding scale_denominator to enable rendering at fixed scale (#1582) From 06ef86a7687ecc6c92b87f18df7db5db7083dcf5 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 21 May 2013 17:02:33 -0700 Subject: [PATCH 062/110] more 2.2 changelog updates --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5eb2d367..ab8a0f7bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ For a complete change history, see the git log. - Added faster reprojection support between `epsg:3857` and `epsg:4326` (#1705,#1703,#1579) -- Fixed concurrency problem when using cursors in postgis plugin (#1823) +- Fixed concurrency problem when using cursors in postgis plugin (#1823,#1588) - Fixed postgres connection pool leaks when using `persist_connection=false` (#1764) @@ -49,6 +49,8 @@ For a complete change history, see the git log. - Faster rendering of rasters by reducing memory allocation of temporary buffers (#1516) +- Fixed some raster reprojection artifacts (#1501) + - Fixed raster alignment when width != height and raster is being scaled (#1748,#1622) - Added support for caching rasters for re-use during rendering when styling more than once per layer (#1543) @@ -152,12 +154,16 @@ For a complete change history, see the git log. - Fixed uneven rendering of markers along lines (#1693) +- Fixed handling of extra bytes in some shapefile fields (#1605) + - Fixed handling (finally) of null shapes and partially corrupt shapefiles (#1630,#1621) - Added ability to re-use `mapnik::image_32` and `mapnik::grid` by exposing a `clear` method (#1571) - Added support for writing RGB (no A) png images by using the format string of `png:t=0` (#1559) +- Added experimental support for geometry simplification at symbolizer level (#1385) + ## Mapnik 2.1.0 Released Aug 23, 2012 From fa2d2a73e068b4971c62ddcec76c48a14c671596 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 21 May 2013 17:41:18 -0700 Subject: [PATCH 063/110] more 2.2 changelog updates including summary - refs #1850 --- CHANGELOG.md | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab8a0f7bf..4f3e83d1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ For a complete change history, see the git log. ## 2.2.0 +Released --- + +(Packaged from ---) + +Summary: The 2.2.0 release is the fastest running and most stable release in the history of Mapnik. The code line represents development in the master branch since the release of 2.1.0 in Aug 2012 and therefore includes nearly a year of bugfixes and low level optimizations. Shapefile and PostGIS datasources have benefited from numerous stability fixes, 64 bit integer support has been added to support OSM data, and many rendering fixes have landed for high quality output when using a rendering `scale_factor`. Many critical code paths have been optimized extensively include raster rendering, xml map loading, string to number conversion, vector reprojection when using `epsg:4326` and `epsg:3857`, `hextree` encoding, halo rendering, and rendering when using a custom `gamma`. Mapnik 2.2 also compiles faster than previous releases in the 2.x series and drops several uneeded and hard to install dependencies. + - Removed 3 depedencies without loosing any functionality: `ltdl`, `cairomm` and `libsigc++` (#1804,#806,#1681) - Added 64 bit integer support in expressions, feature ids, and the grid_renderer (#1661,#1662,#1662) @@ -45,7 +51,7 @@ For a complete change history, see the git log. - Optimized string handling and type conversion by removing `boost::to_lower`, `boost::trim`, and `boost::lexical_cast` usage (#1687,#1687,#1633) -- Optimized alpha preserving hextree method for quantization of png images (#1629) +- Optimized alpha preserving `hextree` method for quantization of png images (#1629) - Faster rendering of rasters by reducing memory allocation of temporary buffers (#1516) @@ -75,7 +81,7 @@ For a complete change history, see the git log. - Fixed error reporting in the python plugin (#1422) -- Added the ability to run test without installing with `make test-local` +- Added the ability to run tests without installing with `make test-local` - Reduced library binary size by adding support for `-fvisibility-inlines-hidden` and `-fvisibility=hidden` (#1826,#1832) @@ -83,15 +89,15 @@ For a complete change history, see the git log. - Added the ability to use `boost::hash` on `mapnik::value` types (#1729) -- Removed obsolete `GEOS` plugin and unmaintained `kismet` plugin (#1809,#1833) +- Removed obsolete `geos` plugin (functionality replaced by `csv` plugin) and unmaintained `kismet` plugin (#1809,#1833) -- Added new `mapnik-config` flags: `--all-flags`, `--defines`, `--git-describe`, `--includes`, `--dep-includes`, `--cxxflags`, `--cxx`. +- Added new `mapnik-config` flags: `--all-flags`, `--defines`, `--git-describe`, `--includes`, `--dep-includes`, `--cxxflags`, `--cxx` (#1443) - Added support for unicode strings as arguments in python bindings (#163) -- Added DebugSymbolizer (#1366) +- Added DebugSymbolizer which is able to render the otherwise invisible collision boxes (#1366) -- Optimized rendering overhead of using `gamma` property (#1174) +- Optimized rendering by reducing overhead of using `gamma` property (#1174) - Fixed rendering artifacts when using `polygon-gamma` or `line-gamma` equal to 0 (#761,#1763) @@ -99,8 +105,6 @@ For a complete change history, see the git log. - Removed the `bind` option for datasources (#1654) -- Added new mapnik-config options: `git-describe`, `defines`, `includes`, `dep-includes`, and `cxxflags` (#1443) - - Added ability to access style list from map by (name,obj) in python (#1725) - Added `is_solid` method to python mapnik.Image and mapnik.ImageView classes (#1728) @@ -130,7 +134,7 @@ For a complete change history, see the git log. - Added support for setting zlib `Z_FIXED` strategy with format string: `png:z=fixed` -- Fixed handling of transparency level option in Octree-based PNG encoding (#1556) +- Fixed handling of transparency level option in `octree` png encoding (#1556) - Added ability to pass a pre-created collision detector to the cairo renderer (#1444) @@ -311,7 +315,7 @@ Released April 10, 2012 - Workaround for boost interprocess compile error with recent gcc versions (#950,#1001,#1082) -- Fix possible memory corruption when using hextree mode for png color reduction (#1087) +- Fix possible memory corruption when using `hextree` mode for png color reduction (#1087) - Fixed bug in shield line placement when dx/dy are used to shift the label relative to the placement point (Matt Amos) (#908) @@ -491,14 +495,14 @@ Released March 23, 2010 - PNG: fixed png256 for large images and some improvements to reduce color corruptions ([#522](https://github.com/mapnik/mapnik/issues/522)) -- PNG: Added new quantization method for indexed png format using hextree with full support for alpha +- PNG: Added new quantization method for indexed png format using `hextree` with full support for alpha channel. Also new method has some optimizations for color gradients common when using elevation based - rasters. By default old method using octree is used. (r1680, r1683, [#477](https://github.com/mapnik/mapnik/issues/477)) + rasters. By default old method using `octree` is used. (r1680, r1683, [#477](https://github.com/mapnik/mapnik/issues/477)) - PNG: Added initial support for passing options to png writter like number of colors, transparency support, quantization method and possibly other in future using type parameter. For example "png8:c=128:t=1:m=h" limits palette to 128 colors, uses only binary transparency (0 - none, - 1 - binary, 2 - full), and new method of quantization using hextree (h - hextree, o - octree). + 1 - binary, 2 - full), and new method of quantization using `hextree` (h - `hextree`, o - `octree`). Existing type "png256" can be also written using "png8:c=256:m=o:t=2" (r1680, r1683, [#477](https://github.com/mapnik/mapnik/issues/477)) From 8634a04938bb0b0bd668dff2bfbe2ac096711f5c Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 21 May 2013 19:21:35 -0700 Subject: [PATCH 064/110] add support for drawing all geometry verticies with debug symbolizer mode=vertex and expose in python - closes #1848 --- bindings/python/mapnik_python.cpp | 2 ++ include/mapnik/debug_symbolizer.hpp | 17 +++++++++++- src/agg/process_debug_symbolizer.cpp | 33 +++++++++++++++++++++--- src/build.py | 1 + src/load_map.cpp | 4 ++- src/xml_tree.cpp | 1 + tests/data/good_maps/line_symbolizer.xml | 2 +- tests/python_tests/object_test.py | 4 +++ 8 files changed, 58 insertions(+), 6 deletions(-) diff --git a/bindings/python/mapnik_python.cpp b/bindings/python/mapnik_python.cpp index 38ae41305..c5da5938c 100644 --- a/bindings/python/mapnik_python.cpp +++ b/bindings/python/mapnik_python.cpp @@ -62,6 +62,7 @@ void export_polygon_pattern_symbolizer(); void export_raster_symbolizer(); void export_text_placement(); void export_shield_symbolizer(); +void export_debug_symbolizer(); void export_font_engine(); void export_projection(); void export_proj_transform(); @@ -463,6 +464,7 @@ BOOST_PYTHON_MODULE(_mapnik) export_raster_symbolizer(); export_text_placement(); export_shield_symbolizer(); + export_debug_symbolizer(); export_font_engine(); export_projection(); export_proj_transform(); diff --git a/include/mapnik/debug_symbolizer.hpp b/include/mapnik/debug_symbolizer.hpp index e9e5d3c2b..225b97c54 100644 --- a/include/mapnik/debug_symbolizer.hpp +++ b/include/mapnik/debug_symbolizer.hpp @@ -30,10 +30,25 @@ namespace mapnik { +enum debug_symbolizer_mode_enum { + DEBUG_SYM_MODE_COLLISION, + DEBUG_SYM_MODE_VERTEX, + debug_symbolizer_mode_enum_MAX +}; + +DEFINE_ENUM( debug_symbolizer_mode_e, debug_symbolizer_mode_enum ); + struct MAPNIK_DECL debug_symbolizer : public symbolizer_base { - debug_symbolizer() : symbolizer_base() {} + debug_symbolizer(); + debug_symbolizer(debug_symbolizer const& rhs); + debug_symbolizer_mode_e get_mode() const; + void set_mode(debug_symbolizer_mode_e mode); + +private: + debug_symbolizer_mode_e mode_; + }; } diff --git a/src/agg/process_debug_symbolizer.cpp b/src/agg/process_debug_symbolizer.cpp index 2bf8e276d..277f7fea6 100644 --- a/src/agg/process_debug_symbolizer.cpp +++ b/src/agg/process_debug_symbolizer.cpp @@ -52,10 +52,37 @@ void agg_renderer::process(debug_symbolizer const& sym, mapnik::feature_impl & feature, proj_transform const& prj_trans) { - label_collision_detector4::query_iterator itr = detector_->begin(), end = detector_->end(); - for (;itr!=end; itr++) + debug_symbolizer_mode_e mode = sym.get_mode(); + if (mode == DEBUG_SYM_MODE_COLLISION) { - draw_rect(pixmap_, itr->box); + label_collision_detector4::query_iterator itr = detector_->begin(), end = detector_->end(); + for (;itr!=end; itr++) + { + draw_rect(pixmap_, itr->box); + } + } + else if (mode == DEBUG_SYM_MODE_VERTEX) + { + for (unsigned i=0; i("mode", DEBUG_SYM_MODE_COLLISION); + symbol.set_mode(mode); rule.append(symbol); } diff --git a/src/xml_tree.cpp b/src/xml_tree.cpp index b9c15d407..53cea607e 100644 --- a/src/xml_tree.cpp +++ b/src/xml_tree.cpp @@ -405,6 +405,7 @@ compile_get_opt_attr(expression_ptr); compile_get_attr(std::string); compile_get_attr(filter_mode_e); compile_get_attr(point_placement_e); +compile_get_attr(debug_symbolizer_mode_e); compile_get_attr(marker_placement_e); compile_get_attr(marker_multi_policy_e); compile_get_attr(pattern_alignment_e); diff --git a/tests/data/good_maps/line_symbolizer.xml b/tests/data/good_maps/line_symbolizer.xml index d58791861..7a4944aea 100644 --- a/tests/data/good_maps/line_symbolizer.xml +++ b/tests/data/good_maps/line_symbolizer.xml @@ -6,7 +6,7 @@ - + diff --git a/tests/python_tests/object_test.py b/tests/python_tests/object_test.py index 0a83ddc95..c18d07e8a 100644 --- a/tests/python_tests/object_test.py +++ b/tests/python_tests/object_test.py @@ -14,6 +14,10 @@ def setup(): # from another directory we need to chdir() os.chdir(execution_path('.')) +def test_debug_symbolizer(): + s = mapnik.DebugSymbolizer() + eq_(s.mode,mapnik.debug_symbolizer_mode.collision) + def test_raster_symbolizer(): s = mapnik.RasterSymbolizer() eq_(s.comp_op,mapnik.CompositeOp.src_over) # note: mode is deprecated From ce34be6aab7baf59633a09c1bb82ace3e5f614d2 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 21 May 2013 20:18:37 -0700 Subject: [PATCH 065/110] fix #1853 --- src/build.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/build.py b/src/build.py index 1c01bd4ed..40381e29f 100644 --- a/src/build.py +++ b/src/build.py @@ -209,10 +209,11 @@ source = Split( ) if env['PLUGIN_LINKING'] == 'static': - lib_env.Append(CPPDEFINES = '-DMAPNIK_STATIC_PLUGINS') + hit = False for plugin in env['REQUESTED_PLUGINS']: details = env['PLUGINS'][plugin] if details['lib'] in env['LIBS'] or not details['lib']: + hit = True lib_env.Append(CPPDEFINES = '-DMAPNIK_STATIC_PLUGIN_%s' % plugin.upper()) plugin_env = SConscript('../plugins/input/%s/build.py' % plugin) if plugin_env.has_key('SOURCES') and plugin_env['SOURCES']: @@ -229,6 +230,8 @@ if env['PLUGIN_LINKING'] == 'static': lib_env.AppendUnique(LIBS=plugin_env['LIBS']) else: print("Notice: dependencies not met for plugin '%s', not building..." % plugin) + if hit: + lib_env.Append(CPPDEFINES = '-DMAPNIK_STATIC_PLUGINS') if env['HAS_CAIRO']: lib_env.AppendUnique(LIBPATH=env['CAIRO_LIBPATHS']) From 1e6e587d6094e7f0652f43dcf46e47b8c6bd1e37 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 21 May 2013 20:27:00 -0700 Subject: [PATCH 066/110] support running tests without any plugins built --- tests/python_tests/layer_buffer_size_test.py | 36 +++++++++++--------- tests/python_tests/palette_test.py | 34 +++++++++--------- tests/python_tests/render_test.py | 34 +++++++++--------- tests/visual_tests/test.py | 5 +++ 4 files changed, 60 insertions(+), 49 deletions(-) diff --git a/tests/python_tests/layer_buffer_size_test.py b/tests/python_tests/layer_buffer_size_test.py index 5d3a9c06c..4e7e76ab4 100644 --- a/tests/python_tests/layer_buffer_size_test.py +++ b/tests/python_tests/layer_buffer_size_test.py @@ -9,23 +9,25 @@ def setup(): # from another directory we need to chdir() os.chdir(execution_path('.')) -# the negative buffer on the layer should -# override the postive map buffer leading -# only one point to be rendered in the map -def test_layer_buffer_size_1(): - m = mapnik.Map(512,512) - eq_(m.buffer_size,0) - mapnik.load_map(m,'../data/good_maps/layer_buffer_size_reduction.xml') - eq_(m.buffer_size,256) - eq_(m.layers[0].buffer_size,-150) - m.zoom_all() - im = mapnik.Image(m.width,m.height) - mapnik.render(m,im) - actual = '/tmp/mapnik-layer-buffer-size.png' - expected = 'images/support/mapnik-layer-buffer-size.png' - im.save(actual) - expected_im = mapnik.Image.open(expected) - eq_(im.tostring(),expected_im.tostring(), 'failed comparing actual (%s) and expected (%s)' % (actual,'tests/python_tests/'+ expected)) +if 'sqlite' in mapnik.DatasourceCache.plugin_names(): + + # the negative buffer on the layer should + # override the postive map buffer leading + # only one point to be rendered in the map + def test_layer_buffer_size_1(): + m = mapnik.Map(512,512) + eq_(m.buffer_size,0) + mapnik.load_map(m,'../data/good_maps/layer_buffer_size_reduction.xml') + eq_(m.buffer_size,256) + eq_(m.layers[0].buffer_size,-150) + m.zoom_all() + im = mapnik.Image(m.width,m.height) + mapnik.render(m,im) + actual = '/tmp/mapnik-layer-buffer-size.png' + expected = 'images/support/mapnik-layer-buffer-size.png' + im.save(actual) + expected_im = mapnik.Image.open(expected) + eq_(im.tostring(),expected_im.tostring(), 'failed comparing actual (%s) and expected (%s)' % (actual,'tests/python_tests/'+ expected)) if __name__ == "__main__": diff --git a/tests/python_tests/palette_test.py b/tests/python_tests/palette_test.py index caa50d604..45cb358b0 100644 --- a/tests/python_tests/palette_test.py +++ b/tests/python_tests/palette_test.py @@ -27,22 +27,24 @@ def test_reading_palettes(): palette = mapnik.Palette('\xff\x00\xff\xff\xff\xff', 'rgb') eq_(palette.to_string(),expected_rgb); -def test_render_with_palette(): - m = mapnik.Map(600,400) - mapnik.load_map(m,'../data/good_maps/agg_poly_gamma_map.xml') - m.zoom_all() - im = mapnik.Image(m.width,m.height) - mapnik.render(m,im) - act = open('../data/palettes/palette256.act','rb') - palette = mapnik.Palette(act.read(),'act') - # test saving directly to filesystem - im.save('/tmp/mapnik-palette-test.png','png',palette) - # test saving to a string - open('/tmp/mapnik-palette-test2.png','wb').write(im.tostring('png',palette)); - # compare the two methods - eq_(mapnik.Image.open('/tmp/mapnik-palette-test.png').tostring(),mapnik.Image.open('/tmp/mapnik-palette-test2.png').tostring(),'%s not eq to %s' % ('/tmp/mapnik-palette-test.png','/tmp/mapnik-palette-test2.png')) - # compare to expected - eq_(mapnik.Image.open('/tmp/mapnik-palette-test.png').tostring(),mapnik.Image.open('./images/support/mapnik-palette-test.png').tostring(),'%s not eq to %s' % ('/tmp/mapnik-palette-test.png','./images/support/mapnik-palette-test.png')) +if 'shape' in mapnik.DatasourceCache.plugin_names(): + + def test_render_with_palette(): + m = mapnik.Map(600,400) + mapnik.load_map(m,'../data/good_maps/agg_poly_gamma_map.xml') + m.zoom_all() + im = mapnik.Image(m.width,m.height) + mapnik.render(m,im) + act = open('../data/palettes/palette256.act','rb') + palette = mapnik.Palette(act.read(),'act') + # test saving directly to filesystem + im.save('/tmp/mapnik-palette-test.png','png',palette) + # test saving to a string + open('/tmp/mapnik-palette-test2.png','wb').write(im.tostring('png',palette)); + # compare the two methods + eq_(mapnik.Image.open('/tmp/mapnik-palette-test.png').tostring(),mapnik.Image.open('/tmp/mapnik-palette-test2.png').tostring(),'%s not eq to %s' % ('/tmp/mapnik-palette-test.png','/tmp/mapnik-palette-test2.png')) + # compare to expected + eq_(mapnik.Image.open('/tmp/mapnik-palette-test.png').tostring(),mapnik.Image.open('./images/support/mapnik-palette-test.png').tostring(),'%s not eq to %s' % ('/tmp/mapnik-palette-test.png','./images/support/mapnik-palette-test.png')) if __name__ == "__main__": setup() diff --git a/tests/python_tests/render_test.py b/tests/python_tests/render_test.py index 0490dafa6..cf8e577a8 100644 --- a/tests/python_tests/render_test.py +++ b/tests/python_tests/render_test.py @@ -173,22 +173,24 @@ def test_render_with_scale_factor_zero_throws(): im = mapnik.Image(256, 256) mapnik.render(m,im,0.0) -def test_render_with_scale_factor(): - m = mapnik.Map(256,256) - mapnik.load_map(m,'../data/good_maps/marker-text-line.xml') - m.zoom_all() - sizes = [.00001,.005,.1,.899,1,1.5,2,5,10,100] - for size in sizes: - im = mapnik.Image(256, 256) - mapnik.render(m,im,size) - expected_file = './images/support/marker-text-line-scale-factor-%s.png' % size - actual_file = '/tmp/' + os.path.basename(expected_file) - im.save(actual_file,'png8') - #im.save(expected_file,'png8') - # we save and re-open here so both png8 images are ready as full color png - actual = mapnik.Image.open(expected_file) - expected = mapnik.Image.open(expected_file) - eq_(actual.tostring(),expected.tostring(), 'failed comparing actual (%s) and expected (%s)' % (actual_file,expected_file)) +if 'shape' in mapnik.DatasourceCache.plugin_names(): + + def test_render_with_scale_factor(): + m = mapnik.Map(256,256) + mapnik.load_map(m,'../data/good_maps/marker-text-line.xml') + m.zoom_all() + sizes = [.00001,.005,.1,.899,1,1.5,2,5,10,100] + for size in sizes: + im = mapnik.Image(256, 256) + mapnik.render(m,im,size) + expected_file = './images/support/marker-text-line-scale-factor-%s.png' % size + actual_file = '/tmp/' + os.path.basename(expected_file) + im.save(actual_file,'png8') + #im.save(expected_file,'png8') + # we save and re-open here so both png8 images are ready as full color png + actual = mapnik.Image.open(expected_file) + expected = mapnik.Image.open(expected_file) + eq_(actual.tostring(),expected.tostring(), 'failed comparing actual (%s) and expected (%s)' % (actual_file,expected_file)) if __name__ == "__main__": setup() diff --git a/tests/visual_tests/test.py b/tests/visual_tests/test.py index ca90e1a0b..d83ec943d 100755 --- a/tests/visual_tests/test.py +++ b/tests/visual_tests/test.py @@ -174,6 +174,9 @@ class Reporting: return item def summary(self): + if self.passed == 0: + print '\nNOTICE: No valid visual tests were run' + return 0 if len(self.errors) == 0: print '\nAll %s visual tests passed: \x1b[1;32m✓ \x1b[0m' % self.passed return 0 @@ -265,6 +268,8 @@ def render(filename,config, width, height, bbox, scale_factor, reporting): else: m.zoom_all() except Exception, e: + if 'Could not create datasource' in str(e): + return m reporting.other_error(filename, repr(e)) return m From e02ba2a4ebef8241c9cfede4d086e25aa451dd30 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 21 May 2013 20:51:17 -0700 Subject: [PATCH 067/110] upgrade bundled scons to 2.3.0 release --- scons/scons-LICENSE | 2 +- scons/scons-README | 3 +- scons/scons-local-2.2.0/SCons/Action.py | 1257 ------- scons/scons-local-2.2.0/SCons/Builder.py | 877 ----- scons/scons-local-2.2.0/SCons/CacheDir.py | 216 -- scons/scons-local-2.2.0/SCons/Conftest.py | 793 ---- scons/scons-local-2.2.0/SCons/Debug.py | 220 -- scons/scons-local-2.2.0/SCons/Defaults.py | 494 --- scons/scons-local-2.2.0/SCons/Environment.py | 2417 ------------ scons/scons-local-2.2.0/SCons/Errors.py | 205 - scons/scons-local-2.2.0/SCons/Executor.py | 633 ---- scons/scons-local-2.2.0/SCons/Job.py | 435 --- scons/scons-local-2.2.0/SCons/Memoize.py | 244 -- scons/scons-local-2.2.0/SCons/Node/Alias.py | 152 - scons/scons-local-2.2.0/SCons/Node/FS.py | 3302 ----------------- scons/scons-local-2.2.0/SCons/Node/Python.py | 128 - .../scons-local-2.2.0/SCons/Node/__init__.py | 1330 ------- .../SCons/Options/BoolOption.py | 50 - .../SCons/Options/EnumOption.py | 50 - .../SCons/Options/ListOption.py | 50 - .../SCons/Options/PackageOption.py | 50 - .../SCons/Options/PathOption.py | 76 - .../SCons/Options/__init__.py | 67 - scons/scons-local-2.2.0/SCons/PathList.py | 231 -- .../SCons/Platform/__init__.py | 241 -- scons/scons-local-2.2.0/SCons/Platform/aix.py | 69 - .../SCons/Platform/cygwin.py | 55 - .../SCons/Platform/darwin.py | 70 - .../scons-local-2.2.0/SCons/Platform/hpux.py | 46 - .../scons-local-2.2.0/SCons/Platform/irix.py | 44 - scons/scons-local-2.2.0/SCons/Platform/os2.py | 58 - .../scons-local-2.2.0/SCons/Platform/posix.py | 263 -- .../scons-local-2.2.0/SCons/Platform/sunos.py | 50 - .../scons-local-2.2.0/SCons/Platform/win32.py | 384 -- scons/scons-local-2.2.0/SCons/SConf.py | 1030 ----- scons/scons-local-2.2.0/SCons/SConsign.py | 389 -- scons/scons-local-2.2.0/SCons/Scanner/C.py | 132 - scons/scons-local-2.2.0/SCons/Scanner/D.py | 73 - scons/scons-local-2.2.0/SCons/Scanner/Dir.py | 109 - .../SCons/Scanner/Fortran.py | 316 -- scons/scons-local-2.2.0/SCons/Scanner/IDL.py | 48 - .../scons-local-2.2.0/SCons/Scanner/LaTeX.py | 390 -- scons/scons-local-2.2.0/SCons/Scanner/Prog.py | 101 - scons/scons-local-2.2.0/SCons/Scanner/RC.py | 55 - .../SCons/Scanner/__init__.py | 413 --- .../SCons/Script/Interactive.py | 384 -- scons/scons-local-2.2.0/SCons/Script/Main.py | 1406 ------- .../SCons/Script/SConsOptions.py | 939 ----- .../SCons/Script/SConscript.py | 640 ---- .../SCons/Script/__init__.py | 412 -- scons/scons-local-2.2.0/SCons/Sig.py | 63 - scons/scons-local-2.2.0/SCons/Subst.py | 904 ----- scons/scons-local-2.2.0/SCons/Taskmaster.py | 1032 ------ scons/scons-local-2.2.0/SCons/Tool/386asm.py | 61 - .../scons-local-2.2.0/SCons/Tool/BitKeeper.py | 67 - scons/scons-local-2.2.0/SCons/Tool/CVS.py | 73 - .../SCons/Tool/FortranCommon.py | 263 -- .../SCons/Tool/GettextCommon.py | 429 --- .../SCons/Tool/JavaCommon.py | 323 -- .../SCons/Tool/MSCommon/__init__.py | 56 - .../SCons/Tool/MSCommon/arch.py | 61 - .../SCons/Tool/MSCommon/common.py | 240 -- .../SCons/Tool/MSCommon/netframework.py | 82 - .../SCons/Tool/MSCommon/sdk.py | 391 -- .../SCons/Tool/MSCommon/vc.py | 464 --- .../SCons/Tool/MSCommon/vs.py | 553 --- .../scons-local-2.2.0/SCons/Tool/Perforce.py | 103 - .../SCons/Tool/PharLapCommon.py | 137 - scons/scons-local-2.2.0/SCons/Tool/RCS.py | 64 - scons/scons-local-2.2.0/SCons/Tool/SCCS.py | 64 - .../SCons/Tool/Subversion.py | 71 - .../scons-local-2.2.0/SCons/Tool/__init__.py | 681 ---- scons/scons-local-2.2.0/SCons/Tool/aixc++.py | 82 - scons/scons-local-2.2.0/SCons/Tool/aixcc.py | 74 - scons/scons-local-2.2.0/SCons/Tool/aixf77.py | 80 - scons/scons-local-2.2.0/SCons/Tool/aixlink.py | 76 - .../scons-local-2.2.0/SCons/Tool/applelink.py | 71 - scons/scons-local-2.2.0/SCons/Tool/ar.py | 63 - scons/scons-local-2.2.0/SCons/Tool/as.py | 78 - scons/scons-local-2.2.0/SCons/Tool/bcc32.py | 81 - scons/scons-local-2.2.0/SCons/Tool/c++.py | 99 - scons/scons-local-2.2.0/SCons/Tool/cc.py | 102 - scons/scons-local-2.2.0/SCons/Tool/cvf.py | 58 - scons/scons-local-2.2.0/SCons/Tool/default.py | 50 - scons/scons-local-2.2.0/SCons/Tool/dmd.py | 240 -- scons/scons-local-2.2.0/SCons/Tool/dvi.py | 64 - scons/scons-local-2.2.0/SCons/Tool/dvipdf.py | 125 - scons/scons-local-2.2.0/SCons/Tool/dvips.py | 95 - scons/scons-local-2.2.0/SCons/Tool/f03.py | 63 - scons/scons-local-2.2.0/SCons/Tool/f77.py | 62 - scons/scons-local-2.2.0/SCons/Tool/f90.py | 62 - scons/scons-local-2.2.0/SCons/Tool/f95.py | 63 - .../SCons/Tool/filesystem.py | 98 - scons/scons-local-2.2.0/SCons/Tool/fortran.py | 62 - scons/scons-local-2.2.0/SCons/Tool/g++.py | 90 - scons/scons-local-2.2.0/SCons/Tool/g77.py | 73 - scons/scons-local-2.2.0/SCons/Tool/gas.py | 53 - scons/scons-local-2.2.0/SCons/Tool/gcc.py | 80 - scons/scons-local-2.2.0/SCons/Tool/gettext.py | 45 - .../scons-local-2.2.0/SCons/Tool/gfortran.py | 64 - scons/scons-local-2.2.0/SCons/Tool/gnulink.py | 62 - scons/scons-local-2.2.0/SCons/Tool/gs.py | 81 - scons/scons-local-2.2.0/SCons/Tool/hpc++.py | 84 - scons/scons-local-2.2.0/SCons/Tool/hpcc.py | 53 - scons/scons-local-2.2.0/SCons/Tool/hplink.py | 77 - scons/scons-local-2.2.0/SCons/Tool/icc.py | 59 - scons/scons-local-2.2.0/SCons/Tool/icl.py | 52 - scons/scons-local-2.2.0/SCons/Tool/ifl.py | 72 - scons/scons-local-2.2.0/SCons/Tool/ifort.py | 88 - scons/scons-local-2.2.0/SCons/Tool/ilink.py | 59 - scons/scons-local-2.2.0/SCons/Tool/ilink32.py | 60 - scons/scons-local-2.2.0/SCons/Tool/install.py | 283 -- scons/scons-local-2.2.0/SCons/Tool/intelc.py | 522 --- scons/scons-local-2.2.0/SCons/Tool/ipkg.py | 67 - scons/scons-local-2.2.0/SCons/Tool/jar.py | 116 - scons/scons-local-2.2.0/SCons/Tool/javac.py | 232 -- scons/scons-local-2.2.0/SCons/Tool/javah.py | 137 - scons/scons-local-2.2.0/SCons/Tool/latex.py | 80 - scons/scons-local-2.2.0/SCons/Tool/lex.py | 97 - scons/scons-local-2.2.0/SCons/Tool/link.py | 122 - scons/scons-local-2.2.0/SCons/Tool/linkloc.py | 112 - scons/scons-local-2.2.0/SCons/Tool/m4.py | 63 - scons/scons-local-2.2.0/SCons/Tool/masm.py | 77 - scons/scons-local-2.2.0/SCons/Tool/midl.py | 88 - scons/scons-local-2.2.0/SCons/Tool/mingw.py | 179 - scons/scons-local-2.2.0/SCons/Tool/msgfmt.py | 102 - scons/scons-local-2.2.0/SCons/Tool/msginit.py | 114 - .../scons-local-2.2.0/SCons/Tool/msgmerge.py | 98 - scons/scons-local-2.2.0/SCons/Tool/mslib.py | 64 - scons/scons-local-2.2.0/SCons/Tool/mslink.py | 327 -- scons/scons-local-2.2.0/SCons/Tool/mssdk.py | 50 - scons/scons-local-2.2.0/SCons/Tool/msvc.py | 278 -- scons/scons-local-2.2.0/SCons/Tool/msvs.py | 1799 --------- scons/scons-local-2.2.0/SCons/Tool/mwcc.py | 207 -- scons/scons-local-2.2.0/SCons/Tool/mwld.py | 107 - scons/scons-local-2.2.0/SCons/Tool/nasm.py | 72 - .../SCons/Tool/packaging/__init__.py | 312 -- .../SCons/Tool/packaging/ipk.py | 185 - .../SCons/Tool/packaging/msi.py | 527 --- .../SCons/Tool/packaging/rpm.py | 365 -- .../SCons/Tool/packaging/src_tarbz2.py | 43 - .../SCons/Tool/packaging/src_targz.py | 43 - .../SCons/Tool/packaging/src_zip.py | 43 - .../SCons/Tool/packaging/tarbz2.py | 44 - .../SCons/Tool/packaging/targz.py | 44 - .../SCons/Tool/packaging/zip.py | 44 - scons/scons-local-2.2.0/SCons/Tool/pdf.py | 78 - .../scons-local-2.2.0/SCons/Tool/pdflatex.py | 84 - scons/scons-local-2.2.0/SCons/Tool/pdftex.py | 109 - scons/scons-local-2.2.0/SCons/Tool/qt.py | 336 -- scons/scons-local-2.2.0/SCons/Tool/rmic.py | 126 - scons/scons-local-2.2.0/SCons/Tool/rpcgen.py | 70 - scons/scons-local-2.2.0/SCons/Tool/rpm.py | 132 - scons/scons-local-2.2.0/SCons/Tool/sgiar.py | 68 - scons/scons-local-2.2.0/SCons/Tool/sgic++.py | 58 - scons/scons-local-2.2.0/SCons/Tool/sgicc.py | 53 - scons/scons-local-2.2.0/SCons/Tool/sgilink.py | 62 - scons/scons-local-2.2.0/SCons/Tool/sunar.py | 67 - scons/scons-local-2.2.0/SCons/Tool/sunc++.py | 142 - scons/scons-local-2.2.0/SCons/Tool/suncc.py | 58 - scons/scons-local-2.2.0/SCons/Tool/sunf77.py | 63 - scons/scons-local-2.2.0/SCons/Tool/sunf90.py | 64 - scons/scons-local-2.2.0/SCons/Tool/sunf95.py | 64 - scons/scons-local-2.2.0/SCons/Tool/sunlink.py | 76 - scons/scons-local-2.2.0/SCons/Tool/swig.py | 183 - scons/scons-local-2.2.0/SCons/Tool/tar.py | 73 - scons/scons-local-2.2.0/SCons/Tool/tex.py | 928 ----- .../scons-local-2.2.0/SCons/Tool/textfile.py | 175 - scons/scons-local-2.2.0/SCons/Tool/tlib.py | 53 - scons/scons-local-2.2.0/SCons/Tool/wix.py | 99 - .../scons-local-2.2.0/SCons/Tool/xgettext.py | 333 -- scons/scons-local-2.2.0/SCons/Tool/yacc.py | 140 - scons/scons-local-2.2.0/SCons/Tool/zip.py | 99 - scons/scons-local-2.2.0/SCons/Util.py | 1492 -------- .../SCons/Variables/BoolVariable.py | 89 - .../SCons/Variables/EnumVariable.py | 103 - .../SCons/Variables/ListVariable.py | 135 - .../SCons/Variables/PackageVariable.py | 106 - .../SCons/Variables/PathVariable.py | 147 - .../SCons/Variables/__init__.py | 312 -- scons/scons-local-2.2.0/SCons/Warnings.py | 246 -- scons/scons-local-2.2.0/SCons/__init__.py | 49 - .../SCons/compat/__init__.py | 237 -- .../SCons/compat/_scons_builtins.py | 150 - .../SCons/compat/_scons_collections.py | 45 - .../SCons/compat/_scons_dbm.py | 45 - .../SCons/compat/_scons_hashlib.py | 76 - .../SCons/compat/_scons_io.py | 45 - .../SCons/compat/_scons_sets.py | 563 --- .../SCons/compat/_scons_subprocess.py | 1281 ------- scons/scons-local-2.2.0/SCons/cpp.py | 589 --- scons/scons-local-2.2.0/SCons/dblite.py | 254 -- scons/scons-local-2.2.0/SCons/exitfuncs.py | 77 - scons/scons-local-2.2.0/scons-2.2.0.egg-info | 13 - scons/scons-time.py | 4 +- scons/scons.py | 40 +- scons/sconsign.py | 12 +- 197 files changed, 32 insertions(+), 47495 deletions(-) delete mode 100644 scons/scons-local-2.2.0/SCons/Action.py delete mode 100644 scons/scons-local-2.2.0/SCons/Builder.py delete mode 100644 scons/scons-local-2.2.0/SCons/CacheDir.py delete mode 100644 scons/scons-local-2.2.0/SCons/Conftest.py delete mode 100644 scons/scons-local-2.2.0/SCons/Debug.py delete mode 100644 scons/scons-local-2.2.0/SCons/Defaults.py delete mode 100644 scons/scons-local-2.2.0/SCons/Environment.py delete mode 100644 scons/scons-local-2.2.0/SCons/Errors.py delete mode 100644 scons/scons-local-2.2.0/SCons/Executor.py delete mode 100644 scons/scons-local-2.2.0/SCons/Job.py delete mode 100644 scons/scons-local-2.2.0/SCons/Memoize.py delete mode 100644 scons/scons-local-2.2.0/SCons/Node/Alias.py delete mode 100644 scons/scons-local-2.2.0/SCons/Node/FS.py delete mode 100644 scons/scons-local-2.2.0/SCons/Node/Python.py delete mode 100644 scons/scons-local-2.2.0/SCons/Node/__init__.py delete mode 100644 scons/scons-local-2.2.0/SCons/Options/BoolOption.py delete mode 100644 scons/scons-local-2.2.0/SCons/Options/EnumOption.py delete mode 100644 scons/scons-local-2.2.0/SCons/Options/ListOption.py delete mode 100644 scons/scons-local-2.2.0/SCons/Options/PackageOption.py delete mode 100644 scons/scons-local-2.2.0/SCons/Options/PathOption.py delete mode 100644 scons/scons-local-2.2.0/SCons/Options/__init__.py delete mode 100644 scons/scons-local-2.2.0/SCons/PathList.py delete mode 100644 scons/scons-local-2.2.0/SCons/Platform/__init__.py delete mode 100644 scons/scons-local-2.2.0/SCons/Platform/aix.py delete mode 100644 scons/scons-local-2.2.0/SCons/Platform/cygwin.py delete mode 100644 scons/scons-local-2.2.0/SCons/Platform/darwin.py delete mode 100644 scons/scons-local-2.2.0/SCons/Platform/hpux.py delete mode 100644 scons/scons-local-2.2.0/SCons/Platform/irix.py delete mode 100644 scons/scons-local-2.2.0/SCons/Platform/os2.py delete mode 100644 scons/scons-local-2.2.0/SCons/Platform/posix.py delete mode 100644 scons/scons-local-2.2.0/SCons/Platform/sunos.py delete mode 100644 scons/scons-local-2.2.0/SCons/Platform/win32.py delete mode 100644 scons/scons-local-2.2.0/SCons/SConf.py delete mode 100644 scons/scons-local-2.2.0/SCons/SConsign.py delete mode 100644 scons/scons-local-2.2.0/SCons/Scanner/C.py delete mode 100644 scons/scons-local-2.2.0/SCons/Scanner/D.py delete mode 100644 scons/scons-local-2.2.0/SCons/Scanner/Dir.py delete mode 100644 scons/scons-local-2.2.0/SCons/Scanner/Fortran.py delete mode 100644 scons/scons-local-2.2.0/SCons/Scanner/IDL.py delete mode 100644 scons/scons-local-2.2.0/SCons/Scanner/LaTeX.py delete mode 100644 scons/scons-local-2.2.0/SCons/Scanner/Prog.py delete mode 100644 scons/scons-local-2.2.0/SCons/Scanner/RC.py delete mode 100644 scons/scons-local-2.2.0/SCons/Scanner/__init__.py delete mode 100644 scons/scons-local-2.2.0/SCons/Script/Interactive.py delete mode 100644 scons/scons-local-2.2.0/SCons/Script/Main.py delete mode 100644 scons/scons-local-2.2.0/SCons/Script/SConsOptions.py delete mode 100644 scons/scons-local-2.2.0/SCons/Script/SConscript.py delete mode 100644 scons/scons-local-2.2.0/SCons/Script/__init__.py delete mode 100644 scons/scons-local-2.2.0/SCons/Sig.py delete mode 100644 scons/scons-local-2.2.0/SCons/Subst.py delete mode 100644 scons/scons-local-2.2.0/SCons/Taskmaster.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/386asm.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/BitKeeper.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/CVS.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/FortranCommon.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/GettextCommon.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/JavaCommon.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/MSCommon/__init__.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/MSCommon/arch.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/MSCommon/common.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/MSCommon/netframework.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/MSCommon/sdk.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/MSCommon/vc.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/MSCommon/vs.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/Perforce.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/PharLapCommon.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/RCS.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/SCCS.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/Subversion.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/__init__.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/aixc++.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/aixcc.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/aixf77.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/aixlink.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/applelink.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/ar.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/as.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/bcc32.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/c++.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/cc.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/cvf.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/default.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/dmd.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/dvi.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/dvipdf.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/dvips.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/f03.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/f77.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/f90.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/f95.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/filesystem.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/fortran.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/g++.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/g77.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/gas.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/gcc.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/gettext.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/gfortran.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/gnulink.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/gs.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/hpc++.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/hpcc.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/hplink.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/icc.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/icl.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/ifl.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/ifort.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/ilink.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/ilink32.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/install.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/intelc.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/ipkg.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/jar.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/javac.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/javah.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/latex.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/lex.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/link.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/linkloc.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/m4.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/masm.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/midl.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/mingw.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/msgfmt.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/msginit.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/msgmerge.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/mslib.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/mslink.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/mssdk.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/msvc.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/msvs.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/mwcc.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/mwld.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/nasm.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/packaging/__init__.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/packaging/ipk.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/packaging/msi.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/packaging/rpm.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/packaging/src_tarbz2.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/packaging/src_targz.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/packaging/src_zip.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/packaging/tarbz2.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/packaging/targz.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/packaging/zip.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/pdf.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/pdflatex.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/pdftex.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/qt.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/rmic.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/rpcgen.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/rpm.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/sgiar.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/sgic++.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/sgicc.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/sgilink.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/sunar.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/sunc++.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/suncc.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/sunf77.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/sunf90.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/sunf95.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/sunlink.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/swig.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/tar.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/tex.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/textfile.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/tlib.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/wix.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/xgettext.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/yacc.py delete mode 100644 scons/scons-local-2.2.0/SCons/Tool/zip.py delete mode 100644 scons/scons-local-2.2.0/SCons/Util.py delete mode 100644 scons/scons-local-2.2.0/SCons/Variables/BoolVariable.py delete mode 100644 scons/scons-local-2.2.0/SCons/Variables/EnumVariable.py delete mode 100644 scons/scons-local-2.2.0/SCons/Variables/ListVariable.py delete mode 100644 scons/scons-local-2.2.0/SCons/Variables/PackageVariable.py delete mode 100644 scons/scons-local-2.2.0/SCons/Variables/PathVariable.py delete mode 100644 scons/scons-local-2.2.0/SCons/Variables/__init__.py delete mode 100644 scons/scons-local-2.2.0/SCons/Warnings.py delete mode 100644 scons/scons-local-2.2.0/SCons/__init__.py delete mode 100644 scons/scons-local-2.2.0/SCons/compat/__init__.py delete mode 100644 scons/scons-local-2.2.0/SCons/compat/_scons_builtins.py delete mode 100644 scons/scons-local-2.2.0/SCons/compat/_scons_collections.py delete mode 100644 scons/scons-local-2.2.0/SCons/compat/_scons_dbm.py delete mode 100644 scons/scons-local-2.2.0/SCons/compat/_scons_hashlib.py delete mode 100644 scons/scons-local-2.2.0/SCons/compat/_scons_io.py delete mode 100644 scons/scons-local-2.2.0/SCons/compat/_scons_sets.py delete mode 100644 scons/scons-local-2.2.0/SCons/compat/_scons_subprocess.py delete mode 100644 scons/scons-local-2.2.0/SCons/cpp.py delete mode 100644 scons/scons-local-2.2.0/SCons/dblite.py delete mode 100644 scons/scons-local-2.2.0/SCons/exitfuncs.py delete mode 100644 scons/scons-local-2.2.0/scons-2.2.0.egg-info diff --git a/scons/scons-LICENSE b/scons/scons-LICENSE index 4df86d1d4..d47d178b8 100644 --- a/scons/scons-LICENSE +++ b/scons/scons-LICENSE @@ -3,7 +3,7 @@ This copyright and license do not apply to any other software with which this software may have been included. -Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation +Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/scons/scons-README b/scons/scons-README index f1d59d4b4..dc2d7dba7 100644 --- a/scons/scons-README +++ b/scons/scons-README @@ -1,4 +1,4 @@ -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation SCons - a software construction tool @@ -202,3 +202,4 @@ With plenty of help from the SCons Development team: Anthony Roach Terrel Shumway + diff --git a/scons/scons-local-2.2.0/SCons/Action.py b/scons/scons-local-2.2.0/SCons/Action.py deleted file mode 100644 index a648c6937..000000000 --- a/scons/scons-local-2.2.0/SCons/Action.py +++ /dev/null @@ -1,1257 +0,0 @@ -"""SCons.Action - -This encapsulates information about executing any sort of action that -can build one or more target Nodes (typically files) from one or more -source Nodes (also typically files) given a specific Environment. - -The base class here is ActionBase. The base class supplies just a few -OO utility methods and some generic methods for displaying information -about an Action in response to the various commands that control printing. - -A second-level base class is _ActionAction. This extends ActionBase -by providing the methods that can be used to show and perform an -action. True Action objects will subclass _ActionAction; Action -factory class objects will subclass ActionBase. - -The heavy lifting is handled by subclasses for the different types of -actions we might execute: - - CommandAction - CommandGeneratorAction - FunctionAction - ListAction - -The subclasses supply the following public interface methods used by -other modules: - - __call__() - THE public interface, "calling" an Action object executes the - command or Python function. This also takes care of printing - a pre-substitution command for debugging purposes. - - get_contents() - Fetches the "contents" of an Action for signature calculation - plus the varlist. This is what gets MD5 checksummed to decide - if a target needs to be rebuilt because its action changed. - - genstring() - Returns a string representation of the Action *without* - command substitution, but allows a CommandGeneratorAction to - generate the right action based on the specified target, - source and env. This is used by the Signature subsystem - (through the Executor) to obtain an (imprecise) representation - of the Action operation for informative purposes. - - -Subclasses also supply the following methods for internal use within -this module: - - __str__() - Returns a string approximation of the Action; no variable - substitution is performed. - - execute() - The internal method that really, truly, actually handles the - execution of a command or Python function. This is used so - that the __call__() methods can take care of displaying any - pre-substitution representations, and *then* execute an action - without worrying about the specific Actions involved. - - get_presig() - Fetches the "contents" of a subclass for signature calculation. - The varlist is added to this to produce the Action's contents. - - strfunction() - Returns a substituted string representation of the Action. - This is used by the _ActionAction.show() command to display the - command/function that will be executed to generate the target(s). - -There is a related independent ActionCaller class that looks like a -regular Action, and which serves as a wrapper for arbitrary functions -that we want to let the user specify the arguments to now, but actually -execute later (when an out-of-date check determines that it's needed to -be executed, for example). Objects of this class are returned by an -ActionFactory class that provides a __call__() method as a convenient -way for wrapping up the functions. - -""" - -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Action.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.compat - -import dis -import os -# compat layer imports "cPickle" for us if it's available. -import pickle -import re -import sys -import subprocess - -from SCons.Debug import logInstanceCreation -import SCons.Errors -import SCons.Executor -import SCons.Util -import SCons.Subst - -# we use these a lot, so try to optimize them -is_String = SCons.Util.is_String -is_List = SCons.Util.is_List - -class _null(object): - pass - -print_actions = 1 -execute_actions = 1 -print_actions_presub = 0 - -def rfile(n): - try: - return n.rfile() - except AttributeError: - return n - -def default_exitstatfunc(s): - return s - -try: - SET_LINENO = dis.SET_LINENO - HAVE_ARGUMENT = dis.HAVE_ARGUMENT -except AttributeError: - remove_set_lineno_codes = lambda x: x -else: - def remove_set_lineno_codes(code): - result = [] - n = len(code) - i = 0 - while i < n: - c = code[i] - op = ord(c) - if op >= HAVE_ARGUMENT: - if op != SET_LINENO: - result.append(code[i:i+3]) - i = i+3 - else: - result.append(c) - i = i+1 - return ''.join(result) - -strip_quotes = re.compile('^[\'"](.*)[\'"]$') - - -def _callable_contents(obj): - """Return the signature contents of a callable Python object. - """ - try: - # Test if obj is a method. - return _function_contents(obj.im_func) - - except AttributeError: - try: - # Test if obj is a callable object. - return _function_contents(obj.__call__.im_func) - - except AttributeError: - try: - # Test if obj is a code object. - return _code_contents(obj) - - except AttributeError: - # Test if obj is a function object. - return _function_contents(obj) - - -def _object_contents(obj): - """Return the signature contents of any Python object. - - We have to handle the case where object contains a code object - since it can be pickled directly. - """ - try: - # Test if obj is a method. - return _function_contents(obj.im_func) - - except AttributeError: - try: - # Test if obj is a callable object. - return _function_contents(obj.__call__.im_func) - - except AttributeError: - try: - # Test if obj is a code object. - return _code_contents(obj) - - except AttributeError: - try: - # Test if obj is a function object. - return _function_contents(obj) - - except AttributeError: - # Should be a pickable Python object. - try: - return pickle.dumps(obj) - except (pickle.PicklingError, TypeError): - # This is weird, but it seems that nested classes - # are unpickable. The Python docs say it should - # always be a PicklingError, but some Python - # versions seem to return TypeError. Just do - # the best we can. - return str(obj) - - -def _code_contents(code): - """Return the signature contents of a code object. - - By providing direct access to the code object of the - function, Python makes this extremely easy. Hooray! - - Unfortunately, older versions of Python include line - number indications in the compiled byte code. Boo! - So we remove the line number byte codes to prevent - recompilations from moving a Python function. - """ - - contents = [] - - # The code contents depends on the number of local variables - # but not their actual names. - contents.append("%s,%s" % (code.co_argcount, len(code.co_varnames))) - try: - contents.append(",%s,%s" % (len(code.co_cellvars), len(code.co_freevars))) - except AttributeError: - # Older versions of Python do not support closures. - contents.append(",0,0") - - # The code contents depends on any constants accessed by the - # function. Note that we have to call _object_contents on each - # constants because the code object of nested functions can - # show-up among the constants. - # - # Note that we also always ignore the first entry of co_consts - # which contains the function doc string. We assume that the - # function does not access its doc string. - contents.append(',(' + ','.join(map(_object_contents,code.co_consts[1:])) + ')') - - # The code contents depends on the variable names used to - # accessed global variable, as changing the variable name changes - # the variable actually accessed and therefore changes the - # function result. - contents.append(',(' + ','.join(map(_object_contents,code.co_names)) + ')') - - - # The code contents depends on its actual code!!! - contents.append(',(' + str(remove_set_lineno_codes(code.co_code)) + ')') - - return ''.join(contents) - - -def _function_contents(func): - """Return the signature contents of a function.""" - - contents = [_code_contents(func.func_code)] - - # The function contents depends on the value of defaults arguments - if func.func_defaults: - contents.append(',(' + ','.join(map(_object_contents,func.func_defaults)) + ')') - else: - contents.append(',()') - - # The function contents depends on the closure captured cell values. - try: - closure = func.func_closure or [] - except AttributeError: - # Older versions of Python do not support closures. - closure = [] - - #xxx = [_object_contents(x.cell_contents) for x in closure] - try: - xxx = [_object_contents(x.cell_contents) for x in closure] - except AttributeError: - xxx = [] - contents.append(',(' + ','.join(xxx) + ')') - - return ''.join(contents) - - -def _actionAppend(act1, act2): - # This function knows how to slap two actions together. - # Mainly, it handles ListActions by concatenating into - # a single ListAction. - a1 = Action(act1) - a2 = Action(act2) - if a1 is None: - return a2 - if a2 is None: - return a1 - if isinstance(a1, ListAction): - if isinstance(a2, ListAction): - return ListAction(a1.list + a2.list) - else: - return ListAction(a1.list + [ a2 ]) - else: - if isinstance(a2, ListAction): - return ListAction([ a1 ] + a2.list) - else: - return ListAction([ a1, a2 ]) - -def _do_create_keywords(args, kw): - """This converts any arguments after the action argument into - their equivalent keywords and adds them to the kw argument. - """ - v = kw.get('varlist', ()) - # prevent varlist="FOO" from being interpreted as ['F', 'O', 'O'] - if is_String(v): v = (v,) - kw['varlist'] = tuple(v) - if args: - # turn positional args into equivalent keywords - cmdstrfunc = args[0] - if cmdstrfunc is None or is_String(cmdstrfunc): - kw['cmdstr'] = cmdstrfunc - elif callable(cmdstrfunc): - kw['strfunction'] = cmdstrfunc - else: - raise SCons.Errors.UserError( - 'Invalid command display variable type. ' - 'You must either pass a string or a callback which ' - 'accepts (target, source, env) as parameters.') - if len(args) > 1: - kw['varlist'] = args[1:] + kw['varlist'] - if kw.get('strfunction', _null) is not _null \ - and kw.get('cmdstr', _null) is not _null: - raise SCons.Errors.UserError( - 'Cannot have both strfunction and cmdstr args to Action()') - -def _do_create_action(act, kw): - """This is the actual "implementation" for the - Action factory method, below. This handles the - fact that passing lists to Action() itself has - different semantics than passing lists as elements - of lists. - - The former will create a ListAction, the latter - will create a CommandAction by converting the inner - list elements to strings.""" - - if isinstance(act, ActionBase): - return act - - if is_List(act): - return CommandAction(act, **kw) - - if callable(act): - try: - gen = kw['generator'] - del kw['generator'] - except KeyError: - gen = 0 - if gen: - action_type = CommandGeneratorAction - else: - action_type = FunctionAction - return action_type(act, kw) - - if is_String(act): - var=SCons.Util.get_environment_var(act) - if var: - # This looks like a string that is purely an Environment - # variable reference, like "$FOO" or "${FOO}". We do - # something special here...we lazily evaluate the contents - # of that Environment variable, so a user could put something - # like a function or a CommandGenerator in that variable - # instead of a string. - return LazyAction(var, kw) - commands = str(act).split('\n') - if len(commands) == 1: - return CommandAction(commands[0], **kw) - # The list of string commands may include a LazyAction, so we - # reprocess them via _do_create_list_action. - return _do_create_list_action(commands, kw) - # Catch a common error case with a nice message: - if isinstance(act, int) or isinstance(act, float): - raise TypeError("Don't know how to create an Action from a number (%s)"%act) - # Else fail silently (???) - return None - -def _do_create_list_action(act, kw): - """A factory for list actions. Convert the input list into Actions - and then wrap them in a ListAction.""" - acts = [] - for a in act: - aa = _do_create_action(a, kw) - if aa is not None: acts.append(aa) - if not acts: - return ListAction([]) - elif len(acts) == 1: - return acts[0] - else: - return ListAction(acts) - -def Action(act, *args, **kw): - """A factory for action objects.""" - # Really simple: the _do_create_* routines do the heavy lifting. - _do_create_keywords(args, kw) - if is_List(act): - return _do_create_list_action(act, kw) - return _do_create_action(act, kw) - -class ActionBase(object): - """Base class for all types of action objects that can be held by - other objects (Builders, Executors, etc.) This provides the - common methods for manipulating and combining those actions.""" - - def __cmp__(self, other): - return cmp(self.__dict__, other) - - def no_batch_key(self, env, target, source): - return None - - batch_key = no_batch_key - - def genstring(self, target, source, env): - return str(self) - - def get_contents(self, target, source, env): - result = [ self.get_presig(target, source, env) ] - # This should never happen, as the Action() factory should wrap - # the varlist, but just in case an action is created directly, - # we duplicate this check here. - vl = self.get_varlist(target, source, env) - if is_String(vl): vl = (vl,) - for v in vl: - result.append(env.subst('${'+v+'}')) - return ''.join(result) - - def __add__(self, other): - return _actionAppend(self, other) - - def __radd__(self, other): - return _actionAppend(other, self) - - def presub_lines(self, env): - # CommandGeneratorAction needs a real environment - # in order to return the proper string here, since - # it may call LazyAction, which looks up a key - # in that env. So we temporarily remember the env here, - # and CommandGeneratorAction will use this env - # when it calls its _generate method. - self.presub_env = env - lines = str(self).split('\n') - self.presub_env = None # don't need this any more - return lines - - def get_varlist(self, target, source, env, executor=None): - return self.varlist - - def get_targets(self, env, executor): - """ - Returns the type of targets ($TARGETS, $CHANGED_TARGETS) used - by this action. - """ - return self.targets - -class _ActionAction(ActionBase): - """Base class for actions that create output objects.""" - def __init__(self, cmdstr=_null, strfunction=_null, varlist=(), - presub=_null, chdir=None, exitstatfunc=None, - batch_key=None, targets='$TARGETS', - **kw): - self.cmdstr = cmdstr - if strfunction is not _null: - if strfunction is None: - self.cmdstr = None - else: - self.strfunction = strfunction - self.varlist = varlist - self.presub = presub - self.chdir = chdir - if not exitstatfunc: - exitstatfunc = default_exitstatfunc - self.exitstatfunc = exitstatfunc - - self.targets = targets - - if batch_key: - if not callable(batch_key): - # They have set batch_key, but not to their own - # callable. The default behavior here will batch - # *all* targets+sources using this action, separated - # for each construction environment. - def default_batch_key(self, env, target, source): - return (id(self), id(env)) - batch_key = default_batch_key - SCons.Util.AddMethod(self, batch_key, 'batch_key') - - def print_cmd_line(self, s, target, source, env): - # In python 3, and in some of our tests, sys.stdout is - # a String io object, and it takes unicode strings only - # In other cases it's a regular Python 2.x file object - # which takes strings (bytes), and if you pass those a - # unicode object they try to decode with 'ascii' codec - # which fails if the cmd line has any hi-bit-set chars. - # This code assumes s is a regular string, but should - # work if it's unicode too. - try: - sys.stdout.write(unicode(s + "\n")) - except UnicodeDecodeError: - sys.stdout.write(s + "\n") - - def __call__(self, target, source, env, - exitstatfunc=_null, - presub=_null, - show=_null, - execute=_null, - chdir=_null, - executor=None): - if not is_List(target): - target = [target] - if not is_List(source): - source = [source] - - if presub is _null: - presub = self.presub - if presub is _null: - presub = print_actions_presub - if exitstatfunc is _null: exitstatfunc = self.exitstatfunc - if show is _null: show = print_actions - if execute is _null: execute = execute_actions - if chdir is _null: chdir = self.chdir - save_cwd = None - if chdir: - save_cwd = os.getcwd() - try: - chdir = str(chdir.abspath) - except AttributeError: - if not is_String(chdir): - if executor: - chdir = str(executor.batches[0].targets[0].dir) - else: - chdir = str(target[0].dir) - if presub: - if executor: - target = executor.get_all_targets() - source = executor.get_all_sources() - t = ' and '.join(map(str, target)) - l = '\n '.join(self.presub_lines(env)) - out = u"Building %s with action:\n %s\n" % (t, l) - sys.stdout.write(out) - cmd = None - if show and self.strfunction: - if executor: - target = executor.get_all_targets() - source = executor.get_all_sources() - try: - cmd = self.strfunction(target, source, env, executor) - except TypeError: - cmd = self.strfunction(target, source, env) - if cmd: - if chdir: - cmd = ('os.chdir(%s)\n' % repr(chdir)) + cmd - try: - get = env.get - except AttributeError: - print_func = self.print_cmd_line - else: - print_func = get('PRINT_CMD_LINE_FUNC') - if not print_func: - print_func = self.print_cmd_line - print_func(cmd, target, source, env) - stat = 0 - if execute: - if chdir: - os.chdir(chdir) - try: - stat = self.execute(target, source, env, executor=executor) - if isinstance(stat, SCons.Errors.BuildError): - s = exitstatfunc(stat.status) - if s: - stat.status = s - else: - stat = s - else: - stat = exitstatfunc(stat) - finally: - if save_cwd: - os.chdir(save_cwd) - if cmd and save_cwd: - print_func('os.chdir(%s)' % repr(save_cwd), target, source, env) - - return stat - - -def _string_from_cmd_list(cmd_list): - """Takes a list of command line arguments and returns a pretty - representation for printing.""" - cl = [] - for arg in map(str, cmd_list): - if ' ' in arg or '\t' in arg: - arg = '"' + arg + '"' - cl.append(arg) - return ' '.join(cl) - -# A fiddlin' little function that has an 'import SCons.Environment' which -# can't be moved to the top level without creating an import loop. Since -# this import creates a local variable named 'SCons', it blocks access to -# the global variable, so we move it here to prevent complaints about local -# variables being used uninitialized. -default_ENV = None -def get_default_ENV(env): - global default_ENV - try: - return env['ENV'] - except KeyError: - if not default_ENV: - import SCons.Environment - # This is a hideously expensive way to get a default shell - # environment. What it really should do is run the platform - # setup to get the default ENV. Fortunately, it's incredibly - # rare for an Environment not to have a shell environment, so - # we're not going to worry about it overmuch. - default_ENV = SCons.Environment.Environment()['ENV'] - return default_ENV - -# This function is still in draft mode. We're going to need something like -# it in the long run as more and more places use subprocess, but I'm sure -# it'll have to be tweaked to get the full desired functionality. -# one special arg (so far?), 'error', to tell what to do with exceptions. -def _subproc(scons_env, cmd, error = 'ignore', **kw): - """Do common setup for a subprocess.Popen() call""" - # allow std{in,out,err} to be "'devnull'" - io = kw.get('stdin') - if is_String(io) and io == 'devnull': - kw['stdin'] = open(os.devnull) - io = kw.get('stdout') - if is_String(io) and io == 'devnull': - kw['stdout'] = open(os.devnull, 'w') - io = kw.get('stderr') - if is_String(io) and io == 'devnull': - kw['stderr'] = open(os.devnull, 'w') - - # Figure out what shell environment to use - ENV = kw.get('env', None) - if ENV is None: ENV = get_default_ENV(scons_env) - - # Ensure that the ENV values are all strings: - new_env = {} - for key, value in ENV.items(): - if is_List(value): - # If the value is a list, then we assume it is a path list, - # because that's a pretty common list-like value to stick - # in an environment variable: - value = SCons.Util.flatten_sequence(value) - new_env[key] = os.pathsep.join(map(str, value)) - else: - # It's either a string or something else. If it's a string, - # we still want to call str() because it might be a *Unicode* - # string, which makes subprocess.Popen() gag. If it isn't a - # string or a list, then we just coerce it to a string, which - # is the proper way to handle Dir and File instances and will - # produce something reasonable for just about everything else: - new_env[key] = str(value) - kw['env'] = new_env - - try: - return subprocess.Popen(cmd, **kw) - except EnvironmentError, e: - if error == 'raise': raise - # return a dummy Popen instance that only returns error - class dummyPopen(object): - def __init__(self, e): self.exception = e - def communicate(self): return ('','') - def wait(self): return -self.exception.errno - stdin = None - class f(object): - def read(self): return '' - def readline(self): return '' - stdout = stderr = f() - return dummyPopen(e) - -class CommandAction(_ActionAction): - """Class for command-execution actions.""" - def __init__(self, cmd, **kw): - # Cmd can actually be a list or a single item; if it's a - # single item it should be the command string to execute; if a - # list then it should be the words of the command string to - # execute. Only a single command should be executed by this - # object; lists of commands should be handled by embedding - # these objects in a ListAction object (which the Action() - # factory above does). cmd will be passed to - # Environment.subst_list() for substituting environment - # variables. - if __debug__: logInstanceCreation(self, 'Action.CommandAction') - - _ActionAction.__init__(self, **kw) - if is_List(cmd): - if list(filter(is_List, cmd)): - raise TypeError("CommandAction should be given only " \ - "a single command") - self.cmd_list = cmd - - def __str__(self): - if is_List(self.cmd_list): - return ' '.join(map(str, self.cmd_list)) - return str(self.cmd_list) - - def process(self, target, source, env, executor=None): - if executor: - result = env.subst_list(self.cmd_list, 0, executor=executor) - else: - result = env.subst_list(self.cmd_list, 0, target, source) - silent = None - ignore = None - while True: - try: c = result[0][0][0] - except IndexError: c = None - if c == '@': silent = 1 - elif c == '-': ignore = 1 - else: break - result[0][0] = result[0][0][1:] - try: - if not result[0][0]: - result[0] = result[0][1:] - except IndexError: - pass - return result, ignore, silent - - def strfunction(self, target, source, env, executor=None): - if self.cmdstr is None: - return None - if self.cmdstr is not _null: - from SCons.Subst import SUBST_RAW - if executor: - c = env.subst(self.cmdstr, SUBST_RAW, executor=executor) - else: - c = env.subst(self.cmdstr, SUBST_RAW, target, source) - if c: - return c - cmd_list, ignore, silent = self.process(target, source, env, executor) - if silent: - return '' - return _string_from_cmd_list(cmd_list[0]) - - def execute(self, target, source, env, executor=None): - """Execute a command action. - - This will handle lists of commands as well as individual commands, - because construction variable substitution may turn a single - "command" into a list. This means that this class can actually - handle lists of commands, even though that's not how we use it - externally. - """ - escape_list = SCons.Subst.escape_list - flatten_sequence = SCons.Util.flatten_sequence - - try: - shell = env['SHELL'] - except KeyError: - raise SCons.Errors.UserError('Missing SHELL construction variable.') - - try: - spawn = env['SPAWN'] - except KeyError: - raise SCons.Errors.UserError('Missing SPAWN construction variable.') - else: - if is_String(spawn): - spawn = env.subst(spawn, raw=1, conv=lambda x: x) - - escape = env.get('ESCAPE', lambda x: x) - - ENV = get_default_ENV(env) - - # Ensure that the ENV values are all strings: - for key, value in ENV.items(): - if not is_String(value): - if is_List(value): - # If the value is a list, then we assume it is a - # path list, because that's a pretty common list-like - # value to stick in an environment variable: - value = flatten_sequence(value) - ENV[key] = os.pathsep.join(map(str, value)) - else: - # If it isn't a string or a list, then we just coerce - # it to a string, which is the proper way to handle - # Dir and File instances and will produce something - # reasonable for just about everything else: - ENV[key] = str(value) - - if executor: - target = executor.get_all_targets() - source = executor.get_all_sources() - cmd_list, ignore, silent = self.process(target, list(map(rfile, source)), env, executor) - - # Use len() to filter out any "command" that's zero-length. - for cmd_line in filter(len, cmd_list): - # Escape the command line for the interpreter we are using. - cmd_line = escape_list(cmd_line, escape) - result = spawn(shell, escape, cmd_line[0], cmd_line, ENV) - if not ignore and result: - msg = "Error %s" % result - return SCons.Errors.BuildError(errstr=msg, - status=result, - action=self, - command=cmd_line) - return 0 - - def get_presig(self, target, source, env, executor=None): - """Return the signature contents of this action's command line. - - This strips $(-$) and everything in between the string, - since those parts don't affect signatures. - """ - from SCons.Subst import SUBST_SIG - cmd = self.cmd_list - if is_List(cmd): - cmd = ' '.join(map(str, cmd)) - else: - cmd = str(cmd) - if executor: - return env.subst_target_source(cmd, SUBST_SIG, executor=executor) - else: - return env.subst_target_source(cmd, SUBST_SIG, target, source) - - def get_implicit_deps(self, target, source, env, executor=None): - icd = env.get('IMPLICIT_COMMAND_DEPENDENCIES', True) - if is_String(icd) and icd[:1] == '$': - icd = env.subst(icd) - if not icd or icd in ('0', 'None'): - return [] - from SCons.Subst import SUBST_SIG - if executor: - cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, executor=executor) - else: - cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, target, source) - res = [] - for cmd_line in cmd_list: - if cmd_line: - d = str(cmd_line[0]) - m = strip_quotes.match(d) - if m: - d = m.group(1) - d = env.WhereIs(d) - if d: - res.append(env.fs.File(d)) - return res - -class CommandGeneratorAction(ActionBase): - """Class for command-generator actions.""" - def __init__(self, generator, kw): - if __debug__: logInstanceCreation(self, 'Action.CommandGeneratorAction') - self.generator = generator - self.gen_kw = kw - self.varlist = kw.get('varlist', ()) - self.targets = kw.get('targets', '$TARGETS') - - def _generate(self, target, source, env, for_signature, executor=None): - # ensure that target is a list, to make it easier to write - # generator functions: - if not is_List(target): - target = [target] - - if executor: - target = executor.get_all_targets() - source = executor.get_all_sources() - ret = self.generator(target=target, - source=source, - env=env, - for_signature=for_signature) - gen_cmd = Action(ret, **self.gen_kw) - if not gen_cmd: - raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret)) - return gen_cmd - - def __str__(self): - try: - env = self.presub_env - except AttributeError: - env = None - if env is None: - env = SCons.Defaults.DefaultEnvironment() - act = self._generate([], [], env, 1) - return str(act) - - def batch_key(self, env, target, source): - return self._generate(target, source, env, 1).batch_key(env, target, source) - - def genstring(self, target, source, env, executor=None): - return self._generate(target, source, env, 1, executor).genstring(target, source, env) - - def __call__(self, target, source, env, exitstatfunc=_null, presub=_null, - show=_null, execute=_null, chdir=_null, executor=None): - act = self._generate(target, source, env, 0, executor) - if act is None: - raise SCons.Errors.UserError("While building `%s': " - "Cannot deduce file extension from source files: %s" - % (repr(list(map(str, target))), repr(list(map(str, source))))) - return act(target, source, env, exitstatfunc, presub, - show, execute, chdir, executor) - - def get_presig(self, target, source, env, executor=None): - """Return the signature contents of this action's command line. - - This strips $(-$) and everything in between the string, - since those parts don't affect signatures. - """ - return self._generate(target, source, env, 1, executor).get_presig(target, source, env) - - def get_implicit_deps(self, target, source, env, executor=None): - return self._generate(target, source, env, 1, executor).get_implicit_deps(target, source, env) - - def get_varlist(self, target, source, env, executor=None): - return self._generate(target, source, env, 1, executor).get_varlist(target, source, env, executor) - - def get_targets(self, env, executor): - return self._generate(None, None, env, 1, executor).get_targets(env, executor) - - - -# A LazyAction is a kind of hybrid generator and command action for -# strings of the form "$VAR". These strings normally expand to other -# strings (think "$CCCOM" to "$CC -c -o $TARGET $SOURCE"), but we also -# want to be able to replace them with functions in the construction -# environment. Consequently, we want lazy evaluation and creation of -# an Action in the case of the function, but that's overkill in the more -# normal case of expansion to other strings. -# -# So we do this with a subclass that's both a generator *and* -# a command action. The overridden methods all do a quick check -# of the construction variable, and if it's a string we just call -# the corresponding CommandAction method to do the heavy lifting. -# If not, then we call the same-named CommandGeneratorAction method. -# The CommandGeneratorAction methods work by using the overridden -# _generate() method, that is, our own way of handling "generation" of -# an action based on what's in the construction variable. - -class LazyAction(CommandGeneratorAction, CommandAction): - - def __init__(self, var, kw): - if __debug__: logInstanceCreation(self, 'Action.LazyAction') - #FUTURE CommandAction.__init__(self, '${'+var+'}', **kw) - CommandAction.__init__(self, '${'+var+'}', **kw) - self.var = SCons.Util.to_String(var) - self.gen_kw = kw - - def get_parent_class(self, env): - c = env.get(self.var) - if is_String(c) and not '\n' in c: - return CommandAction - return CommandGeneratorAction - - def _generate_cache(self, env): - if env: - c = env.get(self.var, '') - else: - c = '' - gen_cmd = Action(c, **self.gen_kw) - if not gen_cmd: - raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c))) - return gen_cmd - - def _generate(self, target, source, env, for_signature, executor=None): - return self._generate_cache(env) - - def __call__(self, target, source, env, *args, **kw): - c = self.get_parent_class(env) - return c.__call__(self, target, source, env, *args, **kw) - - def get_presig(self, target, source, env): - c = self.get_parent_class(env) - return c.get_presig(self, target, source, env) - - def get_varlist(self, target, source, env, executor=None): - c = self.get_parent_class(env) - return c.get_varlist(self, target, source, env, executor) - - -class FunctionAction(_ActionAction): - """Class for Python function actions.""" - - def __init__(self, execfunction, kw): - if __debug__: logInstanceCreation(self, 'Action.FunctionAction') - - self.execfunction = execfunction - try: - self.funccontents = _callable_contents(execfunction) - except AttributeError: - try: - # See if execfunction will do the heavy lifting for us. - self.gc = execfunction.get_contents - except AttributeError: - # This is weird, just do the best we can. - self.funccontents = _object_contents(execfunction) - - _ActionAction.__init__(self, **kw) - - def function_name(self): - try: - return self.execfunction.__name__ - except AttributeError: - try: - return self.execfunction.__class__.__name__ - except AttributeError: - return "unknown_python_function" - - def strfunction(self, target, source, env, executor=None): - if self.cmdstr is None: - return None - if self.cmdstr is not _null: - from SCons.Subst import SUBST_RAW - if executor: - c = env.subst(self.cmdstr, SUBST_RAW, executor=executor) - else: - c = env.subst(self.cmdstr, SUBST_RAW, target, source) - if c: - return c - def array(a): - def quote(s): - try: - str_for_display = s.str_for_display - except AttributeError: - s = repr(s) - else: - s = str_for_display() - return s - return '[' + ", ".join(map(quote, a)) + ']' - try: - strfunc = self.execfunction.strfunction - except AttributeError: - pass - else: - if strfunc is None: - return None - if callable(strfunc): - return strfunc(target, source, env) - name = self.function_name() - tstr = array(target) - sstr = array(source) - return "%s(%s, %s)" % (name, tstr, sstr) - - def __str__(self): - name = self.function_name() - if name == 'ActionCaller': - return str(self.execfunction) - return "%s(target, source, env)" % name - - def execute(self, target, source, env, executor=None): - exc_info = (None,None,None) - try: - if executor: - target = executor.get_all_targets() - source = executor.get_all_sources() - rsources = list(map(rfile, source)) - try: - result = self.execfunction(target=target, source=rsources, env=env) - except KeyboardInterrupt, e: - raise - except SystemExit, e: - raise - except Exception, e: - result = e - exc_info = sys.exc_info() - - if result: - result = SCons.Errors.convert_to_BuildError(result, exc_info) - result.node=target - result.action=self - try: - result.command=self.strfunction(target, source, env, executor) - except TypeError: - result.command=self.strfunction(target, source, env) - - # FIXME: This maintains backward compatibility with respect to - # which type of exceptions were returned by raising an - # exception and which ones were returned by value. It would - # probably be best to always return them by value here, but - # some codes do not check the return value of Actions and I do - # not have the time to modify them at this point. - if (exc_info[1] and - not isinstance(exc_info[1],EnvironmentError)): - raise result - - return result - finally: - # Break the cycle between the traceback object and this - # function stack frame. See the sys.exc_info() doc info for - # more information about this issue. - del exc_info - - - def get_presig(self, target, source, env): - """Return the signature contents of this callable action.""" - try: - return self.gc(target, source, env) - except AttributeError: - return self.funccontents - - def get_implicit_deps(self, target, source, env): - return [] - -class ListAction(ActionBase): - """Class for lists of other actions.""" - def __init__(self, actionlist): - if __debug__: logInstanceCreation(self, 'Action.ListAction') - def list_of_actions(x): - if isinstance(x, ActionBase): - return x - return Action(x) - self.list = list(map(list_of_actions, actionlist)) - # our children will have had any varlist - # applied; we don't need to do it again - self.varlist = () - self.targets = '$TARGETS' - - def genstring(self, target, source, env): - return '\n'.join([a.genstring(target, source, env) for a in self.list]) - - def __str__(self): - return '\n'.join(map(str, self.list)) - - def presub_lines(self, env): - return SCons.Util.flatten_sequence( - [a.presub_lines(env) for a in self.list]) - - def get_presig(self, target, source, env): - """Return the signature contents of this action list. - - Simple concatenation of the signatures of the elements. - """ - return "".join([x.get_contents(target, source, env) for x in self.list]) - - def __call__(self, target, source, env, exitstatfunc=_null, presub=_null, - show=_null, execute=_null, chdir=_null, executor=None): - if executor: - target = executor.get_all_targets() - source = executor.get_all_sources() - for act in self.list: - stat = act(target, source, env, exitstatfunc, presub, - show, execute, chdir, executor) - if stat: - return stat - return 0 - - def get_implicit_deps(self, target, source, env): - result = [] - for act in self.list: - result.extend(act.get_implicit_deps(target, source, env)) - return result - - def get_varlist(self, target, source, env, executor=None): - result = SCons.Util.OrderedDict() - for act in self.list: - for var in act.get_varlist(target, source, env, executor): - result[var] = True - return list(result.keys()) - -class ActionCaller(object): - """A class for delaying calling an Action function with specific - (positional and keyword) arguments until the Action is actually - executed. - - This class looks to the rest of the world like a normal Action object, - but what it's really doing is hanging on to the arguments until we - have a target, source and env to use for the expansion. - """ - def __init__(self, parent, args, kw): - self.parent = parent - self.args = args - self.kw = kw - - def get_contents(self, target, source, env): - actfunc = self.parent.actfunc - try: - # "self.actfunc" is a function. - contents = str(actfunc.func_code.co_code) - except AttributeError: - # "self.actfunc" is a callable object. - try: - contents = str(actfunc.__call__.im_func.func_code.co_code) - except AttributeError: - # No __call__() method, so it might be a builtin - # or something like that. Do the best we can. - contents = str(actfunc) - contents = remove_set_lineno_codes(contents) - return contents - - def subst(self, s, target, source, env): - # If s is a list, recursively apply subst() - # to every element in the list - if is_List(s): - result = [] - for elem in s: - result.append(self.subst(elem, target, source, env)) - return self.parent.convert(result) - - # Special-case hack: Let a custom function wrapped in an - # ActionCaller get at the environment through which the action - # was called by using this hard-coded value as a special return. - if s == '$__env__': - return env - elif is_String(s): - return env.subst(s, 1, target, source) - return self.parent.convert(s) - - def subst_args(self, target, source, env): - return [self.subst(x, target, source, env) for x in self.args] - - def subst_kw(self, target, source, env): - kw = {} - for key in self.kw.keys(): - kw[key] = self.subst(self.kw[key], target, source, env) - return kw - - def __call__(self, target, source, env, executor=None): - args = self.subst_args(target, source, env) - kw = self.subst_kw(target, source, env) - return self.parent.actfunc(*args, **kw) - - def strfunction(self, target, source, env): - args = self.subst_args(target, source, env) - kw = self.subst_kw(target, source, env) - return self.parent.strfunc(*args, **kw) - - def __str__(self): - return self.parent.strfunc(*self.args, **self.kw) - -class ActionFactory(object): - """A factory class that will wrap up an arbitrary function - as an SCons-executable Action object. - - The real heavy lifting here is done by the ActionCaller class. - We just collect the (positional and keyword) arguments that we're - called with and give them to the ActionCaller object we create, - so it can hang onto them until it needs them. - """ - def __init__(self, actfunc, strfunc, convert=lambda x: x): - self.actfunc = actfunc - self.strfunc = strfunc - self.convert = convert - - def __call__(self, *args, **kw): - ac = ActionCaller(self, args, kw) - action = Action(ac, strfunction=ac.strfunction) - return action - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Builder.py b/scons/scons-local-2.2.0/SCons/Builder.py deleted file mode 100644 index 5a8aff1aa..000000000 --- a/scons/scons-local-2.2.0/SCons/Builder.py +++ /dev/null @@ -1,877 +0,0 @@ -"""SCons.Builder - -Builder object subsystem. - -A Builder object is a callable that encapsulates information about how -to execute actions to create a target Node (file) from source Nodes -(files), and how to create those dependencies for tracking. - -The main entry point here is the Builder() factory method. This provides -a procedural interface that creates the right underlying Builder object -based on the keyword arguments supplied and the types of the arguments. - -The goal is for this external interface to be simple enough that the -vast majority of users can create new Builders as necessary to support -building new types of files in their configurations, without having to -dive any deeper into this subsystem. - -The base class here is BuilderBase. This is a concrete base class which -does, in fact, represent the Builder objects that we (or users) create. - -There is also a proxy that looks like a Builder: - - CompositeBuilder - - This proxies for a Builder with an action that is actually a - dictionary that knows how to map file suffixes to a specific - action. This is so that we can invoke different actions - (compilers, compile options) for different flavors of source - files. - -Builders and their proxies have the following public interface methods -used by other modules: - - __call__() - THE public interface. Calling a Builder object (with the - use of internal helper methods) sets up the target and source - dependencies, appropriate mapping to a specific action, and the - environment manipulation necessary for overridden construction - variable. This also takes care of warning about possible mistakes - in keyword arguments. - - add_emitter() - Adds an emitter for a specific file suffix, used by some Tool - modules to specify that (for example) a yacc invocation on a .y - can create a .h *and* a .c file. - - add_action() - Adds an action for a specific file suffix, heavily used by - Tool modules to add their specific action(s) for turning - a source file into an object file to the global static - and shared object file Builders. - -There are the following methods for internal use within this module: - - _execute() - The internal method that handles the heavily lifting when a - Builder is called. This is used so that the __call__() methods - can set up warning about possible mistakes in keyword-argument - overrides, and *then* execute all of the steps necessary so that - the warnings only occur once. - - get_name() - Returns the Builder's name within a specific Environment, - primarily used to try to return helpful information in error - messages. - - adjust_suffix() - get_prefix() - get_suffix() - get_src_suffix() - set_src_suffix() - Miscellaneous stuff for handling the prefix and suffix - manipulation we use in turning source file names into target - file names. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Builder.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import collections - -import SCons.Action -from SCons.Debug import logInstanceCreation -from SCons.Errors import InternalError, UserError -import SCons.Executor -import SCons.Memoize -import SCons.Node -import SCons.Node.FS -import SCons.Util -import SCons.Warnings - -class _Null(object): - pass - -_null = _Null - -def match_splitext(path, suffixes = []): - if suffixes: - matchsuf = [S for S in suffixes if path[-len(S):] == S] - if matchsuf: - suf = max([(len(_f),_f) for _f in matchsuf])[1] - return [path[:-len(suf)], path[-len(suf):]] - return SCons.Util.splitext(path) - -class DictCmdGenerator(SCons.Util.Selector): - """This is a callable class that can be used as a - command generator function. It holds on to a dictionary - mapping file suffixes to Actions. It uses that dictionary - to return the proper action based on the file suffix of - the source file.""" - - def __init__(self, dict=None, source_ext_match=1): - SCons.Util.Selector.__init__(self, dict) - self.source_ext_match = source_ext_match - - def src_suffixes(self): - return list(self.keys()) - - def add_action(self, suffix, action): - """Add a suffix-action pair to the mapping. - """ - self[suffix] = action - - def __call__(self, target, source, env, for_signature): - if not source: - return [] - - if self.source_ext_match: - suffixes = self.src_suffixes() - ext = None - for src in map(str, source): - my_ext = match_splitext(src, suffixes)[1] - if ext and my_ext != ext: - raise UserError("While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s" - % (repr(list(map(str, target))), src, ext, my_ext)) - ext = my_ext - else: - ext = match_splitext(str(source[0]), self.src_suffixes())[1] - - if not ext: - #return ext - raise UserError("While building `%s': " - "Cannot deduce file extension from source files: %s" - % (repr(list(map(str, target))), repr(list(map(str, source))))) - - try: - ret = SCons.Util.Selector.__call__(self, env, source, ext) - except KeyError, e: - raise UserError("Ambiguous suffixes after environment substitution: %s == %s == %s" % (e.args[0], e.args[1], e.args[2])) - if ret is None: - raise UserError("While building `%s' from `%s': Don't know how to build from a source file with suffix `%s'. Expected a suffix in this list: %s." % \ - (repr(list(map(str, target))), repr(list(map(str, source))), ext, repr(list(self.keys())))) - return ret - -class CallableSelector(SCons.Util.Selector): - """A callable dictionary that will, in turn, call the value it - finds if it can.""" - def __call__(self, env, source): - value = SCons.Util.Selector.__call__(self, env, source) - if callable(value): - value = value(env, source) - return value - -class DictEmitter(SCons.Util.Selector): - """A callable dictionary that maps file suffixes to emitters. - When called, it finds the right emitter in its dictionary for the - suffix of the first source file, and calls that emitter to get the - right lists of targets and sources to return. If there's no emitter - for the suffix in its dictionary, the original target and source are - returned. - """ - def __call__(self, target, source, env): - emitter = SCons.Util.Selector.__call__(self, env, source) - if emitter: - target, source = emitter(target, source, env) - return (target, source) - -class ListEmitter(collections.UserList): - """A callable list of emitters that calls each in sequence, - returning the result. - """ - def __call__(self, target, source, env): - for e in self.data: - target, source = e(target, source, env) - return (target, source) - -# These are a common errors when calling a Builder; -# they are similar to the 'target' and 'source' keyword args to builders, -# so we issue warnings when we see them. The warnings can, of course, -# be disabled. -misleading_keywords = { - 'targets' : 'target', - 'sources' : 'source', -} - -class OverrideWarner(collections.UserDict): - """A class for warning about keyword arguments that we use as - overrides in a Builder call. - - This class exists to handle the fact that a single Builder call - can actually invoke multiple builders. This class only emits the - warnings once, no matter how many Builders are invoked. - """ - def __init__(self, dict): - collections.UserDict.__init__(self, dict) - if __debug__: logInstanceCreation(self, 'Builder.OverrideWarner') - self.already_warned = None - def warn(self): - if self.already_warned: - return - for k in self.keys(): - if k in misleading_keywords: - alt = misleading_keywords[k] - msg = "Did you mean to use `%s' instead of `%s'?" % (alt, k) - SCons.Warnings.warn(SCons.Warnings.MisleadingKeywordsWarning, msg) - self.already_warned = 1 - -def Builder(**kw): - """A factory for builder objects.""" - composite = None - if 'generator' in kw: - if 'action' in kw: - raise UserError("You must not specify both an action and a generator.") - kw['action'] = SCons.Action.CommandGeneratorAction(kw['generator'], {}) - del kw['generator'] - elif 'action' in kw: - source_ext_match = kw.get('source_ext_match', 1) - if 'source_ext_match' in kw: - del kw['source_ext_match'] - if SCons.Util.is_Dict(kw['action']): - composite = DictCmdGenerator(kw['action'], source_ext_match) - kw['action'] = SCons.Action.CommandGeneratorAction(composite, {}) - kw['src_suffix'] = composite.src_suffixes() - else: - kw['action'] = SCons.Action.Action(kw['action']) - - if 'emitter' in kw: - emitter = kw['emitter'] - if SCons.Util.is_String(emitter): - # This allows users to pass in an Environment - # variable reference (like "$FOO") as an emitter. - # We will look in that Environment variable for - # a callable to use as the actual emitter. - var = SCons.Util.get_environment_var(emitter) - if not var: - raise UserError("Supplied emitter '%s' does not appear to refer to an Environment variable" % emitter) - kw['emitter'] = EmitterProxy(var) - elif SCons.Util.is_Dict(emitter): - kw['emitter'] = DictEmitter(emitter) - elif SCons.Util.is_List(emitter): - kw['emitter'] = ListEmitter(emitter) - - result = BuilderBase(**kw) - - if not composite is None: - result = CompositeBuilder(result, composite) - - return result - -def _node_errors(builder, env, tlist, slist): - """Validate that the lists of target and source nodes are - legal for this builder and environment. Raise errors or - issue warnings as appropriate. - """ - - # First, figure out if there are any errors in the way the targets - # were specified. - for t in tlist: - if t.side_effect: - raise UserError("Multiple ways to build the same target were specified for: %s" % t) - if t.has_explicit_builder(): - if not t.env is None and not t.env is env: - action = t.builder.action - t_contents = action.get_contents(tlist, slist, t.env) - contents = action.get_contents(tlist, slist, env) - - if t_contents == contents: - msg = "Two different environments were specified for target %s,\n\tbut they appear to have the same action: %s" % (t, action.genstring(tlist, slist, t.env)) - SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarning, msg) - else: - msg = "Two environments with different actions were specified for the same target: %s" % t - raise UserError(msg) - if builder.multi: - if t.builder != builder: - msg = "Two different builders (%s and %s) were specified for the same target: %s" % (t.builder.get_name(env), builder.get_name(env), t) - raise UserError(msg) - # TODO(batch): list constructed each time! - if t.get_executor().get_all_targets() != tlist: - msg = "Two different target lists have a target in common: %s (from %s and from %s)" % (t, list(map(str, t.get_executor().get_all_targets())), list(map(str, tlist))) - raise UserError(msg) - elif t.sources != slist: - msg = "Multiple ways to build the same target were specified for: %s (from %s and from %s)" % (t, list(map(str, t.sources)), list(map(str, slist))) - raise UserError(msg) - - if builder.single_source: - if len(slist) > 1: - raise UserError("More than one source given for single-source builder: targets=%s sources=%s" % (list(map(str,tlist)), list(map(str,slist)))) - -class EmitterProxy(object): - """This is a callable class that can act as a - Builder emitter. It holds on to a string that - is a key into an Environment dictionary, and will - look there at actual build time to see if it holds - a callable. If so, we will call that as the actual - emitter.""" - def __init__(self, var): - self.var = SCons.Util.to_String(var) - - def __call__(self, target, source, env): - emitter = self.var - - # Recursively substitute the variable. - # We can't use env.subst() because it deals only - # in strings. Maybe we should change that? - while SCons.Util.is_String(emitter) and emitter in env: - emitter = env[emitter] - if callable(emitter): - target, source = emitter(target, source, env) - elif SCons.Util.is_List(emitter): - for e in emitter: - target, source = e(target, source, env) - - return (target, source) - - - def __cmp__(self, other): - return cmp(self.var, other.var) - -class BuilderBase(object): - """Base class for Builders, objects that create output - nodes (files) from input nodes (files). - """ - - if SCons.Memoize.use_memoizer: - __metaclass__ = SCons.Memoize.Memoized_Metaclass - - memoizer_counters = [] - - def __init__(self, action = None, - prefix = '', - suffix = '', - src_suffix = '', - target_factory = None, - source_factory = None, - target_scanner = None, - source_scanner = None, - emitter = None, - multi = 0, - env = None, - single_source = 0, - name = None, - chdir = _null, - is_explicit = 1, - src_builder = None, - ensure_suffix = False, - **overrides): - if __debug__: logInstanceCreation(self, 'Builder.BuilderBase') - self._memo = {} - self.action = action - self.multi = multi - if SCons.Util.is_Dict(prefix): - prefix = CallableSelector(prefix) - self.prefix = prefix - if SCons.Util.is_Dict(suffix): - suffix = CallableSelector(suffix) - self.env = env - self.single_source = single_source - if 'overrides' in overrides: - SCons.Warnings.warn(SCons.Warnings.DeprecatedBuilderKeywordsWarning, - "The \"overrides\" keyword to Builder() creation has been deprecated;\n" +\ - "\tspecify the items as keyword arguments to the Builder() call instead.") - overrides.update(overrides['overrides']) - del overrides['overrides'] - if 'scanner' in overrides: - SCons.Warnings.warn(SCons.Warnings.DeprecatedBuilderKeywordsWarning, - "The \"scanner\" keyword to Builder() creation has been deprecated;\n" - "\tuse: source_scanner or target_scanner as appropriate.") - del overrides['scanner'] - self.overrides = overrides - - self.set_suffix(suffix) - self.set_src_suffix(src_suffix) - self.ensure_suffix = ensure_suffix - - self.target_factory = target_factory - self.source_factory = source_factory - self.target_scanner = target_scanner - self.source_scanner = source_scanner - - self.emitter = emitter - - # Optional Builder name should only be used for Builders - # that don't get attached to construction environments. - if name: - self.name = name - self.executor_kw = {} - if not chdir is _null: - self.executor_kw['chdir'] = chdir - self.is_explicit = is_explicit - - if src_builder is None: - src_builder = [] - elif not SCons.Util.is_List(src_builder): - src_builder = [ src_builder ] - self.src_builder = src_builder - - def __nonzero__(self): - raise InternalError("Do not test for the Node.builder attribute directly; use Node.has_builder() instead") - - def get_name(self, env): - """Attempts to get the name of the Builder. - - Look at the BUILDERS variable of env, expecting it to be a - dictionary containing this Builder, and return the key of the - dictionary. If there's no key, then return a directly-configured - name (if there is one) or the name of the class (by default).""" - - try: - index = list(env['BUILDERS'].values()).index(self) - return list(env['BUILDERS'].keys())[index] - except (AttributeError, KeyError, TypeError, ValueError): - try: - return self.name - except AttributeError: - return str(self.__class__) - - def __cmp__(self, other): - return cmp(self.__dict__, other.__dict__) - - def splitext(self, path, env=None): - if not env: - env = self.env - if env: - suffixes = self.src_suffixes(env) - else: - suffixes = [] - return match_splitext(path, suffixes) - - def _adjustixes(self, files, pre, suf, ensure_suffix=False): - if not files: - return [] - result = [] - if not SCons.Util.is_List(files): - files = [files] - - for f in files: - if SCons.Util.is_String(f): - f = SCons.Util.adjustixes(f, pre, suf, ensure_suffix) - result.append(f) - return result - - def _create_nodes(self, env, target = None, source = None): - """Create and return lists of target and source nodes. - """ - src_suf = self.get_src_suffix(env) - - target_factory = env.get_factory(self.target_factory) - source_factory = env.get_factory(self.source_factory) - - source = self._adjustixes(source, None, src_suf) - slist = env.arg2nodes(source, source_factory) - - pre = self.get_prefix(env, slist) - suf = self.get_suffix(env, slist) - - if target is None: - try: - t_from_s = slist[0].target_from_source - except AttributeError: - raise UserError("Do not know how to create a target from source `%s'" % slist[0]) - except IndexError: - tlist = [] - else: - splitext = lambda S: self.splitext(S,env) - tlist = [ t_from_s(pre, suf, splitext) ] - else: - target = self._adjustixes(target, pre, suf, self.ensure_suffix) - tlist = env.arg2nodes(target, target_factory, target=target, source=source) - - if self.emitter: - # The emitter is going to do str(node), but because we're - # being called *from* a builder invocation, the new targets - # don't yet have a builder set on them and will look like - # source files. Fool the emitter's str() calls by setting - # up a temporary builder on the new targets. - new_targets = [] - for t in tlist: - if not t.is_derived(): - t.builder_set(self) - new_targets.append(t) - - orig_tlist = tlist[:] - orig_slist = slist[:] - - target, source = self.emitter(target=tlist, source=slist, env=env) - - # Now delete the temporary builders that we attached to any - # new targets, so that _node_errors() doesn't do weird stuff - # to them because it thinks they already have builders. - for t in new_targets: - if t.builder is self: - # Only delete the temporary builder if the emitter - # didn't change it on us. - t.builder_set(None) - - # Have to call arg2nodes yet again, since it is legal for - # emitters to spit out strings as well as Node instances. - tlist = env.arg2nodes(target, target_factory, - target=orig_tlist, source=orig_slist) - slist = env.arg2nodes(source, source_factory, - target=orig_tlist, source=orig_slist) - - return tlist, slist - - def _execute(self, env, target, source, overwarn={}, executor_kw={}): - # We now assume that target and source are lists or None. - if self.src_builder: - source = self.src_builder_sources(env, source, overwarn) - - if self.single_source and len(source) > 1 and target is None: - result = [] - if target is None: target = [None]*len(source) - for tgt, src in zip(target, source): - if not tgt is None: tgt = [tgt] - if not src is None: src = [src] - result.extend(self._execute(env, tgt, src, overwarn)) - return SCons.Node.NodeList(result) - - overwarn.warn() - - tlist, slist = self._create_nodes(env, target, source) - - # Check for errors with the specified target/source lists. - _node_errors(self, env, tlist, slist) - - # The targets are fine, so find or make the appropriate Executor to - # build this particular list of targets from this particular list of - # sources. - - executor = None - key = None - - if self.multi: - try: - executor = tlist[0].get_executor(create = 0) - except (AttributeError, IndexError): - pass - else: - executor.add_sources(slist) - - if executor is None: - if not self.action: - fmt = "Builder %s must have an action to build %s." - raise UserError(fmt % (self.get_name(env or self.env), - list(map(str,tlist)))) - key = self.action.batch_key(env or self.env, tlist, slist) - if key: - try: - executor = SCons.Executor.GetBatchExecutor(key) - except KeyError: - pass - else: - executor.add_batch(tlist, slist) - - if executor is None: - executor = SCons.Executor.Executor(self.action, env, [], - tlist, slist, executor_kw) - if key: - SCons.Executor.AddBatchExecutor(key, executor) - - # Now set up the relevant information in the target Nodes themselves. - for t in tlist: - t.cwd = env.fs.getcwd() - t.builder_set(self) - t.env_set(env) - t.add_source(slist) - t.set_executor(executor) - t.set_explicit(self.is_explicit) - - return SCons.Node.NodeList(tlist) - - def __call__(self, env, target=None, source=None, chdir=_null, **kw): - # We now assume that target and source are lists or None. - # The caller (typically Environment.BuilderWrapper) is - # responsible for converting any scalar values to lists. - if chdir is _null: - ekw = self.executor_kw - else: - ekw = self.executor_kw.copy() - ekw['chdir'] = chdir - if kw: - if 'srcdir' in kw: - def prependDirIfRelative(f, srcdir=kw['srcdir']): - import os.path - if SCons.Util.is_String(f) and not os.path.isabs(f): - f = os.path.join(srcdir, f) - return f - if not SCons.Util.is_List(source): - source = [source] - source = list(map(prependDirIfRelative, source)) - del kw['srcdir'] - if self.overrides: - env_kw = self.overrides.copy() - env_kw.update(kw) - else: - env_kw = kw - else: - env_kw = self.overrides - env = env.Override(env_kw) - return self._execute(env, target, source, OverrideWarner(kw), ekw) - - def adjust_suffix(self, suff): - if suff and not suff[0] in [ '.', '_', '$' ]: - return '.' + suff - return suff - - def get_prefix(self, env, sources=[]): - prefix = self.prefix - if callable(prefix): - prefix = prefix(env, sources) - return env.subst(prefix) - - def set_suffix(self, suffix): - if not callable(suffix): - suffix = self.adjust_suffix(suffix) - self.suffix = suffix - - def get_suffix(self, env, sources=[]): - suffix = self.suffix - if callable(suffix): - suffix = suffix(env, sources) - return env.subst(suffix) - - def set_src_suffix(self, src_suffix): - if not src_suffix: - src_suffix = [] - elif not SCons.Util.is_List(src_suffix): - src_suffix = [ src_suffix ] - self.src_suffix = [callable(suf) and suf or self.adjust_suffix(suf) for suf in src_suffix] - - def get_src_suffix(self, env): - """Get the first src_suffix in the list of src_suffixes.""" - ret = self.src_suffixes(env) - if not ret: - return '' - return ret[0] - - def add_emitter(self, suffix, emitter): - """Add a suffix-emitter mapping to this Builder. - - This assumes that emitter has been initialized with an - appropriate dictionary type, and will throw a TypeError if - not, so the caller is responsible for knowing that this is an - appropriate method to call for the Builder in question. - """ - self.emitter[suffix] = emitter - - def add_src_builder(self, builder): - """ - Add a new Builder to the list of src_builders. - - This requires wiping out cached values so that the computed - lists of source suffixes get re-calculated. - """ - self._memo = {} - self.src_builder.append(builder) - - def _get_sdict(self, env): - """ - Returns a dictionary mapping all of the source suffixes of all - src_builders of this Builder to the underlying Builder that - should be called first. - - This dictionary is used for each target specified, so we save a - lot of extra computation by memoizing it for each construction - environment. - - Note that this is re-computed each time, not cached, because there - might be changes to one of our source Builders (or one of their - source Builders, and so on, and so on...) that we can't "see." - - The underlying methods we call cache their computed values, - though, so we hope repeatedly aggregating them into a dictionary - like this won't be too big a hit. We may need to look for a - better way to do this if performance data show this has turned - into a significant bottleneck. - """ - sdict = {} - for bld in self.get_src_builders(env): - for suf in bld.src_suffixes(env): - sdict[suf] = bld - return sdict - - def src_builder_sources(self, env, source, overwarn={}): - sdict = self._get_sdict(env) - - src_suffixes = self.src_suffixes(env) - - lengths = list(set(map(len, src_suffixes))) - - def match_src_suffix(name, src_suffixes=src_suffixes, lengths=lengths): - node_suffixes = [name[-l:] for l in lengths] - for suf in src_suffixes: - if suf in node_suffixes: - return suf - return None - - result = [] - for s in SCons.Util.flatten(source): - if SCons.Util.is_String(s): - match_suffix = match_src_suffix(env.subst(s)) - if not match_suffix and not '.' in s: - src_suf = self.get_src_suffix(env) - s = self._adjustixes(s, None, src_suf)[0] - else: - match_suffix = match_src_suffix(s.name) - if match_suffix: - try: - bld = sdict[match_suffix] - except KeyError: - result.append(s) - else: - tlist = bld._execute(env, None, [s], overwarn) - # If the subsidiary Builder returned more than one - # target, then filter out any sources that this - # Builder isn't capable of building. - if len(tlist) > 1: - tlist = [t for t in tlist if match_src_suffix(t.name)] - result.extend(tlist) - else: - result.append(s) - - source_factory = env.get_factory(self.source_factory) - - return env.arg2nodes(result, source_factory) - - def _get_src_builders_key(self, env): - return id(env) - - memoizer_counters.append(SCons.Memoize.CountDict('get_src_builders', _get_src_builders_key)) - - def get_src_builders(self, env): - """ - Returns the list of source Builders for this Builder. - - This exists mainly to look up Builders referenced as - strings in the 'BUILDER' variable of the construction - environment and cache the result. - """ - memo_key = id(env) - try: - memo_dict = self._memo['get_src_builders'] - except KeyError: - memo_dict = {} - self._memo['get_src_builders'] = memo_dict - else: - try: - return memo_dict[memo_key] - except KeyError: - pass - - builders = [] - for bld in self.src_builder: - if SCons.Util.is_String(bld): - try: - bld = env['BUILDERS'][bld] - except KeyError: - continue - builders.append(bld) - - memo_dict[memo_key] = builders - return builders - - def _subst_src_suffixes_key(self, env): - return id(env) - - memoizer_counters.append(SCons.Memoize.CountDict('subst_src_suffixes', _subst_src_suffixes_key)) - - def subst_src_suffixes(self, env): - """ - The suffix list may contain construction variable expansions, - so we have to evaluate the individual strings. To avoid doing - this over and over, we memoize the results for each construction - environment. - """ - memo_key = id(env) - try: - memo_dict = self._memo['subst_src_suffixes'] - except KeyError: - memo_dict = {} - self._memo['subst_src_suffixes'] = memo_dict - else: - try: - return memo_dict[memo_key] - except KeyError: - pass - suffixes = [env.subst(x) for x in self.src_suffix] - memo_dict[memo_key] = suffixes - return suffixes - - def src_suffixes(self, env): - """ - Returns the list of source suffixes for all src_builders of this - Builder. - - This is essentially a recursive descent of the src_builder "tree." - (This value isn't cached because there may be changes in a - src_builder many levels deep that we can't see.) - """ - sdict = {} - suffixes = self.subst_src_suffixes(env) - for s in suffixes: - sdict[s] = 1 - for builder in self.get_src_builders(env): - for s in builder.src_suffixes(env): - if s not in sdict: - sdict[s] = 1 - suffixes.append(s) - return suffixes - -class CompositeBuilder(SCons.Util.Proxy): - """A Builder Proxy whose main purpose is to always have - a DictCmdGenerator as its action, and to provide access - to the DictCmdGenerator's add_action() method. - """ - - def __init__(self, builder, cmdgen): - if __debug__: logInstanceCreation(self, 'Builder.CompositeBuilder') - SCons.Util.Proxy.__init__(self, builder) - - # cmdgen should always be an instance of DictCmdGenerator. - self.cmdgen = cmdgen - self.builder = builder - - __call__ = SCons.Util.Delegate('__call__') - - def add_action(self, suffix, action): - self.cmdgen.add_action(suffix, action) - self.set_src_suffix(self.cmdgen.src_suffixes()) - -def is_a_Builder(obj): - """"Returns True iff the specified obj is one of our Builder classes. - - The test is complicated a bit by the fact that CompositeBuilder - is a proxy, not a subclass of BuilderBase. - """ - return (isinstance(obj, BuilderBase) - or isinstance(obj, CompositeBuilder) - or callable(obj)) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/CacheDir.py b/scons/scons-local-2.2.0/SCons/CacheDir.py deleted file mode 100644 index ec7e9eebf..000000000 --- a/scons/scons-local-2.2.0/SCons/CacheDir.py +++ /dev/null @@ -1,216 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/CacheDir.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -__doc__ = """ -CacheDir support -""" - -import os.path -import stat -import sys - -import SCons.Action - -cache_enabled = True -cache_debug = False -cache_force = False -cache_show = False - -def CacheRetrieveFunc(target, source, env): - t = target[0] - fs = t.fs - cd = env.get_CacheDir() - cachedir, cachefile = cd.cachepath(t) - if not fs.exists(cachefile): - cd.CacheDebug('CacheRetrieve(%s): %s not in cache\n', t, cachefile) - return 1 - cd.CacheDebug('CacheRetrieve(%s): retrieving from %s\n', t, cachefile) - if SCons.Action.execute_actions: - if fs.islink(cachefile): - fs.symlink(fs.readlink(cachefile), t.path) - else: - env.copy_from_cache(cachefile, t.path) - st = fs.stat(cachefile) - fs.chmod(t.path, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) - return 0 - -def CacheRetrieveString(target, source, env): - t = target[0] - fs = t.fs - cd = env.get_CacheDir() - cachedir, cachefile = cd.cachepath(t) - if t.fs.exists(cachefile): - return "Retrieved `%s' from cache" % t.path - return None - -CacheRetrieve = SCons.Action.Action(CacheRetrieveFunc, CacheRetrieveString) - -CacheRetrieveSilent = SCons.Action.Action(CacheRetrieveFunc, None) - -def CachePushFunc(target, source, env): - t = target[0] - if t.nocache: - return - fs = t.fs - cd = env.get_CacheDir() - cachedir, cachefile = cd.cachepath(t) - if fs.exists(cachefile): - # Don't bother copying it if it's already there. Note that - # usually this "shouldn't happen" because if the file already - # existed in cache, we'd have retrieved the file from there, - # not built it. This can happen, though, in a race, if some - # other person running the same build pushes their copy to - # the cache after we decide we need to build it but before our - # build completes. - cd.CacheDebug('CachePush(%s): %s already exists in cache\n', t, cachefile) - return - - cd.CacheDebug('CachePush(%s): pushing to %s\n', t, cachefile) - - tempfile = cachefile+'.tmp'+str(os.getpid()) - errfmt = "Unable to copy %s to cache. Cache file is %s" - - if not fs.isdir(cachedir): - try: - fs.makedirs(cachedir) - except EnvironmentError: - # We may have received an exception because another process - # has beaten us creating the directory. - if not fs.isdir(cachedir): - msg = errfmt % (str(target), cachefile) - raise SCons.Errors.EnvironmentError(msg) - - try: - if fs.islink(t.path): - fs.symlink(fs.readlink(t.path), tempfile) - else: - fs.copy2(t.path, tempfile) - fs.rename(tempfile, cachefile) - st = fs.stat(t.path) - fs.chmod(cachefile, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) - except EnvironmentError: - # It's possible someone else tried writing the file at the - # same time we did, or else that there was some problem like - # the CacheDir being on a separate file system that's full. - # In any case, inability to push a file to cache doesn't affect - # the correctness of the build, so just print a warning. - msg = errfmt % (str(target), cachefile) - SCons.Warnings.warn(SCons.Warnings.CacheWriteErrorWarning, msg) - -CachePush = SCons.Action.Action(CachePushFunc, None) - -class CacheDir(object): - - def __init__(self, path): - try: - import hashlib - except ImportError: - msg = "No hashlib or MD5 module available, CacheDir() not supported" - SCons.Warnings.warn(SCons.Warnings.NoMD5ModuleWarning, msg) - self.path = None - else: - self.path = path - self.current_cache_debug = None - self.debugFP = None - - def CacheDebug(self, fmt, target, cachefile): - if cache_debug != self.current_cache_debug: - if cache_debug == '-': - self.debugFP = sys.stdout - elif cache_debug: - self.debugFP = open(cache_debug, 'w') - else: - self.debugFP = None - self.current_cache_debug = cache_debug - if self.debugFP: - self.debugFP.write(fmt % (target, os.path.split(cachefile)[1])) - - def is_enabled(self): - return (cache_enabled and not self.path is None) - - def cachepath(self, node): - """ - """ - if not self.is_enabled(): - return None, None - - sig = node.get_cachedir_bsig() - subdir = sig[0].upper() - dir = os.path.join(self.path, subdir) - return dir, os.path.join(dir, sig) - - def retrieve(self, node): - """ - This method is called from multiple threads in a parallel build, - so only do thread safe stuff here. Do thread unsafe stuff in - built(). - - Note that there's a special trick here with the execute flag - (one that's not normally done for other actions). Basically - if the user requested a no_exec (-n) build, then - SCons.Action.execute_actions is set to 0 and when any action - is called, it does its showing but then just returns zero - instead of actually calling the action execution operation. - The problem for caching is that if the file does NOT exist in - cache then the CacheRetrieveString won't return anything to - show for the task, but the Action.__call__ won't call - CacheRetrieveFunc; instead it just returns zero, which makes - the code below think that the file *was* successfully - retrieved from the cache, therefore it doesn't do any - subsequent building. However, the CacheRetrieveString didn't - print anything because it didn't actually exist in the cache, - and no more build actions will be performed, so the user just - sees nothing. The fix is to tell Action.__call__ to always - execute the CacheRetrieveFunc and then have the latter - explicitly check SCons.Action.execute_actions itself. - """ - if not self.is_enabled(): - return False - - env = node.get_build_env() - if cache_show: - if CacheRetrieveSilent(node, [], env, execute=1) == 0: - node.build(presub=0, execute=0) - return True - else: - if CacheRetrieve(node, [], env, execute=1) == 0: - return True - - return False - - def push(self, node): - if not self.is_enabled(): - return - return CachePush(node, [], node.get_build_env()) - - def push_if_forced(self, node): - if cache_force: - return self.push(node) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Conftest.py b/scons/scons-local-2.2.0/SCons/Conftest.py deleted file mode 100644 index d4662780c..000000000 --- a/scons/scons-local-2.2.0/SCons/Conftest.py +++ /dev/null @@ -1,793 +0,0 @@ -"""SCons.Conftest - -Autoconf-like configuration support; low level implementation of tests. -""" - -# -# Copyright (c) 2003 Stichting NLnet Labs -# Copyright (c) 2001, 2002, 2003 Steven Knight -# -# 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. -# - -# -# The purpose of this module is to define how a check is to be performed. -# Use one of the Check...() functions below. -# - -# -# A context class is used that defines functions for carrying out the tests, -# logging and messages. The following methods and members must be present: -# -# context.Display(msg) Function called to print messages that are normally -# displayed for the user. Newlines are explicitly used. -# The text should also be written to the logfile! -# -# context.Log(msg) Function called to write to a log file. -# -# context.BuildProg(text, ext) -# Function called to build a program, using "ext" for the -# file extention. Must return an empty string for -# success, an error message for failure. -# For reliable test results building should be done just -# like an actual program would be build, using the same -# command and arguments (including configure results so -# far). -# -# context.CompileProg(text, ext) -# Function called to compile a program, using "ext" for -# the file extention. Must return an empty string for -# success, an error message for failure. -# For reliable test results compiling should be done just -# like an actual source file would be compiled, using the -# same command and arguments (including configure results -# so far). -# -# context.AppendLIBS(lib_name_list) -# Append "lib_name_list" to the value of LIBS. -# "lib_namelist" is a list of strings. -# Return the value of LIBS before changing it (any type -# can be used, it is passed to SetLIBS() later.) -# -# context.PrependLIBS(lib_name_list) -# Prepend "lib_name_list" to the value of LIBS. -# "lib_namelist" is a list of strings. -# Return the value of LIBS before changing it (any type -# can be used, it is passed to SetLIBS() later.) -# -# context.SetLIBS(value) -# Set LIBS to "value". The type of "value" is what -# AppendLIBS() returned. -# Return the value of LIBS before changing it (any type -# can be used, it is passed to SetLIBS() later.) -# -# context.headerfilename -# Name of file to append configure results to, usually -# "confdefs.h". -# The file must not exist or be empty when starting. -# Empty or None to skip this (some tests will not work!). -# -# context.config_h (may be missing). If present, must be a string, which -# will be filled with the contents of a config_h file. -# -# context.vardict Dictionary holding variables used for the tests and -# stores results from the tests, used for the build -# commands. -# Normally contains "CC", "LIBS", "CPPFLAGS", etc. -# -# context.havedict Dictionary holding results from the tests that are to -# be used inside a program. -# Names often start with "HAVE_". These are zero -# (feature not present) or one (feature present). Other -# variables may have any value, e.g., "PERLVERSION" can -# be a number and "SYSTEMNAME" a string. -# - -import re -from types import IntType - -# -# PUBLIC VARIABLES -# - -LogInputFiles = 1 # Set that to log the input files in case of a failed test -LogErrorMessages = 1 # Set that to log Conftest-generated error messages - -# -# PUBLIC FUNCTIONS -# - -# Generic remarks: -# - When a language is specified which is not supported the test fails. The -# message is a bit different, because not all the arguments for the normal -# message are available yet (chicken-egg problem). - - -def CheckBuilder(context, text = None, language = None): - """ - Configure check to see if the compiler works. - Note that this uses the current value of compiler and linker flags, make - sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. - "language" should be "C" or "C++" and is used to select the compiler. - Default is "C". - "text" may be used to specify the code to be build. - Returns an empty string for success, an error message for failure. - """ - lang, suffix, msg = _lang2suffix(language) - if msg: - context.Display("%s\n" % msg) - return msg - - if not text: - text = """ -int main() { - return 0; -} -""" - - context.Display("Checking if building a %s file works... " % lang) - ret = context.BuildProg(text, suffix) - _YesNoResult(context, ret, None, text) - return ret - -def CheckCC(context): - """ - Configure check for a working C compiler. - - This checks whether the C compiler, as defined in the $CC construction - variable, can compile a C source file. It uses the current $CCCOM value - too, so that it can test against non working flags. - - """ - context.Display("Checking whether the C compiler works") - text = """ -int main() -{ - return 0; -} -""" - ret = _check_empty_program(context, 'CC', text, 'C') - _YesNoResult(context, ret, None, text) - return ret - -def CheckSHCC(context): - """ - Configure check for a working shared C compiler. - - This checks whether the C compiler, as defined in the $SHCC construction - variable, can compile a C source file. It uses the current $SHCCCOM value - too, so that it can test against non working flags. - - """ - context.Display("Checking whether the (shared) C compiler works") - text = """ -int foo() -{ - return 0; -} -""" - ret = _check_empty_program(context, 'SHCC', text, 'C', use_shared = True) - _YesNoResult(context, ret, None, text) - return ret - -def CheckCXX(context): - """ - Configure check for a working CXX compiler. - - This checks whether the CXX compiler, as defined in the $CXX construction - variable, can compile a CXX source file. It uses the current $CXXCOM value - too, so that it can test against non working flags. - - """ - context.Display("Checking whether the C++ compiler works") - text = """ -int main() -{ - return 0; -} -""" - ret = _check_empty_program(context, 'CXX', text, 'C++') - _YesNoResult(context, ret, None, text) - return ret - -def CheckSHCXX(context): - """ - Configure check for a working shared CXX compiler. - - This checks whether the CXX compiler, as defined in the $SHCXX construction - variable, can compile a CXX source file. It uses the current $SHCXXCOM value - too, so that it can test against non working flags. - - """ - context.Display("Checking whether the (shared) C++ compiler works") - text = """ -int main() -{ - return 0; -} -""" - ret = _check_empty_program(context, 'SHCXX', text, 'C++', use_shared = True) - _YesNoResult(context, ret, None, text) - return ret - -def _check_empty_program(context, comp, text, language, use_shared = False): - """Return 0 on success, 1 otherwise.""" - if comp not in context.env or not context.env[comp]: - # The compiler construction variable is not set or empty - return 1 - - lang, suffix, msg = _lang2suffix(language) - if msg: - return 1 - - if use_shared: - return context.CompileSharedObject(text, suffix) - else: - return context.CompileProg(text, suffix) - - -def CheckFunc(context, function_name, header = None, language = None): - """ - Configure check for a function "function_name". - "language" should be "C" or "C++" and is used to select the compiler. - Default is "C". - Optional "header" can be defined to define a function prototype, include a - header file or anything else that comes before main(). - Sets HAVE_function_name in context.havedict according to the result. - Note that this uses the current value of compiler and linker flags, make - sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. - Returns an empty string for success, an error message for failure. - """ - - # Remarks from autoconf: - # - Don't include because on OSF/1 3.0 it includes - # which includes which contains a prototype for select. - # Similarly for bzero. - # - assert.h is included to define __stub macros and hopefully few - # prototypes, which can conflict with char $1(); below. - # - Override any gcc2 internal prototype to avoid an error. - # - We use char for the function declaration because int might match the - # return type of a gcc2 builtin and then its argument prototype would - # still apply. - # - The GNU C library defines this for functions which it implements to - # always fail with ENOSYS. Some functions are actually named something - # starting with __ and the normal name is an alias. - - if context.headerfilename: - includetext = '#include "%s"' % context.headerfilename - else: - includetext = '' - if not header: - header = """ -#ifdef __cplusplus -extern "C" -#endif -char %s();""" % function_name - - lang, suffix, msg = _lang2suffix(language) - if msg: - context.Display("Cannot check for %s(): %s\n" % (function_name, msg)) - return msg - - text = """ -%(include)s -#include -%(hdr)s - -int main() { -#if defined (__stub_%(name)s) || defined (__stub___%(name)s) - fail fail fail -#else - %(name)s(); -#endif - - return 0; -} -""" % { 'name': function_name, - 'include': includetext, - 'hdr': header } - - context.Display("Checking for %s function %s()... " % (lang, function_name)) - ret = context.BuildProg(text, suffix) - _YesNoResult(context, ret, "HAVE_" + function_name, text, - "Define to 1 if the system has the function `%s'." %\ - function_name) - return ret - - -def CheckHeader(context, header_name, header = None, language = None, - include_quotes = None): - """ - Configure check for a C or C++ header file "header_name". - Optional "header" can be defined to do something before including the - header file (unusual, supported for consistency). - "language" should be "C" or "C++" and is used to select the compiler. - Default is "C". - Sets HAVE_header_name in context.havedict according to the result. - Note that this uses the current value of compiler and linker flags, make - sure $CFLAGS and $CPPFLAGS are set correctly. - Returns an empty string for success, an error message for failure. - """ - # Why compile the program instead of just running the preprocessor? - # It is possible that the header file exists, but actually using it may - # fail (e.g., because it depends on other header files). Thus this test is - # more strict. It may require using the "header" argument. - # - # Use <> by default, because the check is normally used for system header - # files. SCons passes '""' to overrule this. - - # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. - if context.headerfilename: - includetext = '#include "%s"\n' % context.headerfilename - else: - includetext = '' - if not header: - header = "" - - lang, suffix, msg = _lang2suffix(language) - if msg: - context.Display("Cannot check for header file %s: %s\n" - % (header_name, msg)) - return msg - - if not include_quotes: - include_quotes = "<>" - - text = "%s%s\n#include %s%s%s\n\n" % (includetext, header, - include_quotes[0], header_name, include_quotes[1]) - - context.Display("Checking for %s header file %s... " % (lang, header_name)) - ret = context.CompileProg(text, suffix) - _YesNoResult(context, ret, "HAVE_" + header_name, text, - "Define to 1 if you have the <%s> header file." % header_name) - return ret - - -def CheckType(context, type_name, fallback = None, - header = None, language = None): - """ - Configure check for a C or C++ type "type_name". - Optional "header" can be defined to include a header file. - "language" should be "C" or "C++" and is used to select the compiler. - Default is "C". - Sets HAVE_type_name in context.havedict according to the result. - Note that this uses the current value of compiler and linker flags, make - sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. - Returns an empty string for success, an error message for failure. - """ - - # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. - if context.headerfilename: - includetext = '#include "%s"' % context.headerfilename - else: - includetext = '' - if not header: - header = "" - - lang, suffix, msg = _lang2suffix(language) - if msg: - context.Display("Cannot check for %s type: %s\n" % (type_name, msg)) - return msg - - # Remarks from autoconf about this test: - # - Grepping for the type in include files is not reliable (grep isn't - # portable anyway). - # - Using "TYPE my_var;" doesn't work for const qualified types in C++. - # Adding an initializer is not valid for some C++ classes. - # - Using the type as parameter to a function either fails for K&$ C or for - # C++. - # - Using "TYPE *my_var;" is valid in C for some types that are not - # declared (struct something). - # - Using "sizeof(TYPE)" is valid when TYPE is actually a variable. - # - Using the previous two together works reliably. - text = """ -%(include)s -%(header)s - -int main() { - if ((%(name)s *) 0) - return 0; - if (sizeof (%(name)s)) - return 0; -} -""" % { 'include': includetext, - 'header': header, - 'name': type_name } - - context.Display("Checking for %s type %s... " % (lang, type_name)) - ret = context.BuildProg(text, suffix) - _YesNoResult(context, ret, "HAVE_" + type_name, text, - "Define to 1 if the system has the type `%s'." % type_name) - if ret and fallback and context.headerfilename: - f = open(context.headerfilename, "a") - f.write("typedef %s %s;\n" % (fallback, type_name)) - f.close() - - return ret - -def CheckTypeSize(context, type_name, header = None, language = None, expect = None): - """This check can be used to get the size of a given type, or to check whether - the type is of expected size. - - Arguments: - - type : str - the type to check - - includes : sequence - list of headers to include in the test code before testing the type - - language : str - 'C' or 'C++' - - expect : int - if given, will test wether the type has the given number of bytes. - If not given, will automatically find the size. - - Returns: - status : int - 0 if the check failed, or the found size of the type if the check succeeded.""" - - # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. - if context.headerfilename: - includetext = '#include "%s"' % context.headerfilename - else: - includetext = '' - - if not header: - header = "" - - lang, suffix, msg = _lang2suffix(language) - if msg: - context.Display("Cannot check for %s type: %s\n" % (type_name, msg)) - return msg - - src = includetext + header - if not expect is None: - # Only check if the given size is the right one - context.Display('Checking %s is %d bytes... ' % (type_name, expect)) - - # test code taken from autoconf: this is a pretty clever hack to find that - # a type is of a given size using only compilation. This speeds things up - # quite a bit compared to straightforward code using TryRun - src = src + r""" -typedef %s scons_check_type; - -int main() -{ - static int test_array[1 - 2 * !(((long int) (sizeof(scons_check_type))) == %d)]; - test_array[0] = 0; - - return 0; -} -""" - - st = context.CompileProg(src % (type_name, expect), suffix) - if not st: - context.Display("yes\n") - _Have(context, "SIZEOF_%s" % type_name, expect, - "The size of `%s', as computed by sizeof." % type_name) - return expect - else: - context.Display("no\n") - _LogFailed(context, src, st) - return 0 - else: - # Only check if the given size is the right one - context.Message('Checking size of %s ... ' % type_name) - - # We have to be careful with the program we wish to test here since - # compilation will be attempted using the current environment's flags. - # So make sure that the program will compile without any warning. For - # example using: 'int main(int argc, char** argv)' will fail with the - # '-Wall -Werror' flags since the variables argc and argv would not be - # used in the program... - # - src = src + """ -#include -#include -int main() { - printf("%d", (int)sizeof(""" + type_name + """)); - return 0; -} - """ - st, out = context.RunProg(src, suffix) - try: - size = int(out) - except ValueError: - # If cannot convert output of test prog to an integer (the size), - # something went wront, so just fail - st = 1 - size = 0 - - if not st: - context.Display("yes\n") - _Have(context, "SIZEOF_%s" % type_name, size, - "The size of `%s', as computed by sizeof." % type_name) - return size - else: - context.Display("no\n") - _LogFailed(context, src, st) - return 0 - - return 0 - -def CheckDeclaration(context, symbol, includes = None, language = None): - """Checks whether symbol is declared. - - Use the same test as autoconf, that is test whether the symbol is defined - as a macro or can be used as an r-value. - - Arguments: - symbol : str - the symbol to check - includes : str - Optional "header" can be defined to include a header file. - language : str - only C and C++ supported. - - Returns: - status : bool - True if the check failed, False if succeeded.""" - - # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. - if context.headerfilename: - includetext = '#include "%s"' % context.headerfilename - else: - includetext = '' - - if not includes: - includes = "" - - lang, suffix, msg = _lang2suffix(language) - if msg: - context.Display("Cannot check for declaration %s: %s\n" % (symbol, msg)) - return msg - - src = includetext + includes - context.Display('Checking whether %s is declared... ' % symbol) - - src = src + r""" -int main() -{ -#ifndef %s - (void) %s; -#endif - ; - return 0; -} -""" % (symbol, symbol) - - st = context.CompileProg(src, suffix) - _YesNoResult(context, st, "HAVE_DECL_" + symbol, src, - "Set to 1 if %s is defined." % symbol) - return st - -def CheckLib(context, libs, func_name = None, header = None, - extra_libs = None, call = None, language = None, autoadd = 1, - append = True): - """ - Configure check for a C or C++ libraries "libs". Searches through - the list of libraries, until one is found where the test succeeds. - Tests if "func_name" or "call" exists in the library. Note: if it exists - in another library the test succeeds anyway! - Optional "header" can be defined to include a header file. If not given a - default prototype for "func_name" is added. - Optional "extra_libs" is a list of library names to be added after - "lib_name" in the build command. To be used for libraries that "lib_name" - depends on. - Optional "call" replaces the call to "func_name" in the test code. It must - consist of complete C statements, including a trailing ";". - Both "func_name" and "call" arguments are optional, and in that case, just - linking against the libs is tested. - "language" should be "C" or "C++" and is used to select the compiler. - Default is "C". - Note that this uses the current value of compiler and linker flags, make - sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. - Returns an empty string for success, an error message for failure. - """ - # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. - if context.headerfilename: - includetext = '#include "%s"' % context.headerfilename - else: - includetext = '' - if not header: - header = "" - - text = """ -%s -%s""" % (includetext, header) - - # Add a function declaration if needed. - if func_name and func_name != "main": - if not header: - text = text + """ -#ifdef __cplusplus -extern "C" -#endif -char %s(); -""" % func_name - - # The actual test code. - if not call: - call = "%s();" % func_name - - # if no function to test, leave main() blank - text = text + """ -int -main() { - %s -return 0; -} -""" % (call or "") - - if call: - i = call.find("\n") - if i > 0: - calltext = call[:i] + ".." - elif call[-1] == ';': - calltext = call[:-1] - else: - calltext = call - - for lib_name in libs: - - lang, suffix, msg = _lang2suffix(language) - if msg: - context.Display("Cannot check for library %s: %s\n" % (lib_name, msg)) - return msg - - # if a function was specified to run in main(), say it - if call: - context.Display("Checking for %s in %s library %s... " - % (calltext, lang, lib_name)) - # otherwise, just say the name of library and language - else: - context.Display("Checking for %s library %s... " - % (lang, lib_name)) - - if lib_name: - l = [ lib_name ] - if extra_libs: - l.extend(extra_libs) - if append: - oldLIBS = context.AppendLIBS(l) - else: - oldLIBS = context.PrependLIBS(l) - sym = "HAVE_LIB" + lib_name - else: - oldLIBS = -1 - sym = None - - ret = context.BuildProg(text, suffix) - - _YesNoResult(context, ret, sym, text, - "Define to 1 if you have the `%s' library." % lib_name) - if oldLIBS != -1 and (ret or not autoadd): - context.SetLIBS(oldLIBS) - - if not ret: - return ret - - return ret - -# -# END OF PUBLIC FUNCTIONS -# - -def _YesNoResult(context, ret, key, text, comment = None): - """ - Handle the result of a test with a "yes" or "no" result. - "ret" is the return value: empty if OK, error message when not. - "key" is the name of the symbol to be defined (HAVE_foo). - "text" is the source code of the program used for testing. - "comment" is the C comment to add above the line defining the symbol (the - comment is automatically put inside a /* */). If None, no comment is added. - """ - if key: - _Have(context, key, not ret, comment) - if ret: - context.Display("no\n") - _LogFailed(context, text, ret) - else: - context.Display("yes\n") - - -def _Have(context, key, have, comment = None): - """ - Store result of a test in context.havedict and context.headerfilename. - "key" is a "HAVE_abc" name. It is turned into all CAPITALS and non- - alphanumerics are replaced by an underscore. - The value of "have" can be: - 1 - Feature is defined, add "#define key". - 0 - Feature is not defined, add "/* #undef key */". - Adding "undef" is what autoconf does. Not useful for the - compiler, but it shows that the test was done. - number - Feature is defined to this number "#define key have". - Doesn't work for 0 or 1, use a string then. - string - Feature is defined to this string "#define key have". - Give "have" as is should appear in the header file, include quotes - when desired and escape special characters! - """ - key_up = key.upper() - key_up = re.sub('[^A-Z0-9_]', '_', key_up) - context.havedict[key_up] = have - if have == 1: - line = "#define %s 1\n" % key_up - elif have == 0: - line = "/* #undef %s */\n" % key_up - elif isinstance(have, IntType): - line = "#define %s %d\n" % (key_up, have) - else: - line = "#define %s %s\n" % (key_up, str(have)) - - if comment is not None: - lines = "\n/* %s */\n" % comment + line - else: - lines = "\n" + line - - if context.headerfilename: - f = open(context.headerfilename, "a") - f.write(lines) - f.close() - elif hasattr(context,'config_h'): - context.config_h = context.config_h + lines - - -def _LogFailed(context, text, msg): - """ - Write to the log about a failed program. - Add line numbers, so that error messages can be understood. - """ - if LogInputFiles: - context.Log("Failed program was:\n") - lines = text.split('\n') - if len(lines) and lines[-1] == '': - lines = lines[:-1] # remove trailing empty line - n = 1 - for line in lines: - context.Log("%d: %s\n" % (n, line)) - n = n + 1 - if LogErrorMessages: - context.Log("Error message: %s\n" % msg) - - -def _lang2suffix(lang): - """ - Convert a language name to a suffix. - When "lang" is empty or None C is assumed. - Returns a tuple (lang, suffix, None) when it works. - For an unrecognized language returns (None, None, msg). - Where: - lang = the unified language name - suffix = the suffix, including the leading dot - msg = an error message - """ - if not lang or lang in ["C", "c"]: - return ("C", ".c", None) - if lang in ["c++", "C++", "cpp", "CXX", "cxx"]: - return ("C++", ".cpp", None) - - return None, None, "Unsupported language: %s" % lang - - -# vim: set sw=4 et sts=4 tw=79 fo+=l: - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Debug.py b/scons/scons-local-2.2.0/SCons/Debug.py deleted file mode 100644 index 5f1b87c13..000000000 --- a/scons/scons-local-2.2.0/SCons/Debug.py +++ /dev/null @@ -1,220 +0,0 @@ -"""SCons.Debug - -Code for debugging SCons internal things. Shouldn't be -needed by most users. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Debug.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os -import sys -import time -import weakref - -tracked_classes = {} - -def logInstanceCreation(instance, name=None): - if name is None: - name = instance.__class__.__name__ - if name not in tracked_classes: - tracked_classes[name] = [] - tracked_classes[name].append(weakref.ref(instance)) - -def string_to_classes(s): - if s == '*': - return sorted(tracked_classes.keys()) - else: - return s.split() - -def fetchLoggedInstances(classes="*"): - classnames = string_to_classes(classes) - return [(cn, len(tracked_classes[cn])) for cn in classnames] - -def countLoggedInstances(classes, file=sys.stdout): - for classname in string_to_classes(classes): - file.write("%s: %d\n" % (classname, len(tracked_classes[classname]))) - -def listLoggedInstances(classes, file=sys.stdout): - for classname in string_to_classes(classes): - file.write('\n%s:\n' % classname) - for ref in tracked_classes[classname]: - obj = ref() - if obj is not None: - file.write(' %s\n' % repr(obj)) - -def dumpLoggedInstances(classes, file=sys.stdout): - for classname in string_to_classes(classes): - file.write('\n%s:\n' % classname) - for ref in tracked_classes[classname]: - obj = ref() - if obj is not None: - file.write(' %s:\n' % obj) - for key, value in obj.__dict__.items(): - file.write(' %20s : %s\n' % (key, value)) - - - -if sys.platform[:5] == "linux": - # Linux doesn't actually support memory usage stats from getrusage(). - def memory(): - mstr = open('/proc/self/stat').read() - mstr = mstr.split()[22] - return int(mstr) -elif sys.platform[:6] == 'darwin': - #TODO really get memory stats for OS X - def memory(): - return 0 -else: - try: - import resource - except ImportError: - try: - import win32process - import win32api - except ImportError: - def memory(): - return 0 - else: - def memory(): - process_handle = win32api.GetCurrentProcess() - memory_info = win32process.GetProcessMemoryInfo( process_handle ) - return memory_info['PeakWorkingSetSize'] - else: - def memory(): - res = resource.getrusage(resource.RUSAGE_SELF) - return res[4] - -# returns caller's stack -def caller_stack(*backlist): - import traceback - if not backlist: - backlist = [0] - result = [] - for back in backlist: - tb = traceback.extract_stack(limit=3+back) - key = tb[0][:3] - result.append('%s:%d(%s)' % func_shorten(key)) - return result - -caller_bases = {} -caller_dicts = {} - -# trace a caller's stack -def caller_trace(back=0): - import traceback - tb = traceback.extract_stack(limit=3+back) - tb.reverse() - callee = tb[1][:3] - caller_bases[callee] = caller_bases.get(callee, 0) + 1 - for caller in tb[2:]: - caller = callee + caller[:3] - try: - entry = caller_dicts[callee] - except KeyError: - caller_dicts[callee] = entry = {} - entry[caller] = entry.get(caller, 0) + 1 - callee = caller - -# print a single caller and its callers, if any -def _dump_one_caller(key, file, level=0): - leader = ' '*level - for v,c in sorted([(-v,c) for c,v in caller_dicts[key].items()]): - file.write("%s %6d %s:%d(%s)\n" % ((leader,-v) + func_shorten(c[-3:]))) - if c in caller_dicts: - _dump_one_caller(c, file, level+1) - -# print each call tree -def dump_caller_counts(file=sys.stdout): - for k in sorted(caller_bases.keys()): - file.write("Callers of %s:%d(%s), %d calls:\n" - % (func_shorten(k) + (caller_bases[k],))) - _dump_one_caller(k, file) - -shorten_list = [ - ( '/scons/SCons/', 1), - ( '/src/engine/SCons/', 1), - ( '/usr/lib/python', 0), -] - -if os.sep != '/': - shorten_list = [(t[0].replace('/', os.sep), t[1]) for t in shorten_list] - -def func_shorten(func_tuple): - f = func_tuple[0] - for t in shorten_list: - i = f.find(t[0]) - if i >= 0: - if t[1]: - i = i + len(t[0]) - return (f[i:],)+func_tuple[1:] - return func_tuple - - -TraceFP = {} -if sys.platform == 'win32': - TraceDefault = 'con' -else: - TraceDefault = '/dev/tty' - -TimeStampDefault = None -StartTime = time.time() -PreviousTime = StartTime - -def Trace(msg, file=None, mode='w', tstamp=None): - """Write a trace message to a file. Whenever a file is specified, - it becomes the default for the next call to Trace().""" - global TraceDefault - global TimeStampDefault - global PreviousTime - if file is None: - file = TraceDefault - else: - TraceDefault = file - if tstamp is None: - tstamp = TimeStampDefault - else: - TimeStampDefault = tstamp - try: - fp = TraceFP[file] - except KeyError: - try: - fp = TraceFP[file] = open(file, mode) - except TypeError: - # Assume we were passed an open file pointer. - fp = file - if tstamp: - now = time.time() - fp.write('%8.4f %8.4f: ' % (now - StartTime, now - PreviousTime)) - PreviousTime = now - fp.write(msg) - fp.flush() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Defaults.py b/scons/scons-local-2.2.0/SCons/Defaults.py deleted file mode 100644 index 96760cedc..000000000 --- a/scons/scons-local-2.2.0/SCons/Defaults.py +++ /dev/null @@ -1,494 +0,0 @@ -"""SCons.Defaults - -Builders and other things for the local site. Here's where we'll -duplicate the functionality of autoconf until we move it into the -installation procedure or use something like qmconf. - -The code that reads the registry to find MSVC components was borrowed -from distutils.msvccompiler. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# -from __future__ import division - -__revision__ = "src/engine/SCons/Defaults.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - - -import os -import errno -import shutil -import stat -import time -import sys - -import SCons.Action -import SCons.Builder -import SCons.CacheDir -import SCons.Environment -import SCons.PathList -import SCons.Subst -import SCons.Tool - -# A placeholder for a default Environment (for fetching source files -# from source code management systems and the like). This must be -# initialized later, after the top-level directory is set by the calling -# interface. -_default_env = None - -# Lazily instantiate the default environment so the overhead of creating -# it doesn't apply when it's not needed. -def _fetch_DefaultEnvironment(*args, **kw): - """ - Returns the already-created default construction environment. - """ - global _default_env - return _default_env - -def DefaultEnvironment(*args, **kw): - """ - Initial public entry point for creating the default construction - Environment. - - After creating the environment, we overwrite our name - (DefaultEnvironment) with the _fetch_DefaultEnvironment() function, - which more efficiently returns the initialized default construction - environment without checking for its existence. - - (This function still exists with its _default_check because someone - else (*cough* Script/__init__.py *cough*) may keep a reference - to this function. So we can't use the fully functional idiom of - having the name originally be a something that *only* creates the - construction environment and then overwrites the name.) - """ - global _default_env - if not _default_env: - import SCons.Util - _default_env = SCons.Environment.Environment(*args, **kw) - if SCons.Util.md5: - _default_env.Decider('MD5') - else: - _default_env.Decider('timestamp-match') - global DefaultEnvironment - DefaultEnvironment = _fetch_DefaultEnvironment - _default_env._CacheDir_path = None - return _default_env - -# Emitters for setting the shared attribute on object files, -# and an action for checking that all of the source files -# going into a shared library are, in fact, shared. -def StaticObjectEmitter(target, source, env): - for tgt in target: - tgt.attributes.shared = None - return (target, source) - -def SharedObjectEmitter(target, source, env): - for tgt in target: - tgt.attributes.shared = 1 - return (target, source) - -def SharedFlagChecker(source, target, env): - same = env.subst('$STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME') - if same == '0' or same == '' or same == 'False': - for src in source: - try: - shared = src.attributes.shared - except AttributeError: - shared = None - if not shared: - raise SCons.Errors.UserError("Source file: %s is static and is not compatible with shared target: %s" % (src, target[0])) - -SharedCheck = SCons.Action.Action(SharedFlagChecker, None) - -# Some people were using these variable name before we made -# SourceFileScanner part of the public interface. Don't break their -# SConscript files until we've given them some fair warning and a -# transition period. -CScan = SCons.Tool.CScanner -DScan = SCons.Tool.DScanner -LaTeXScan = SCons.Tool.LaTeXScanner -ObjSourceScan = SCons.Tool.SourceFileScanner -ProgScan = SCons.Tool.ProgramScanner - -# These aren't really tool scanners, so they don't quite belong with -# the rest of those in Tool/__init__.py, but I'm not sure where else -# they should go. Leave them here for now. -import SCons.Scanner.Dir -DirScanner = SCons.Scanner.Dir.DirScanner() -DirEntryScanner = SCons.Scanner.Dir.DirEntryScanner() - -# Actions for common languages. -CAction = SCons.Action.Action("$CCCOM", "$CCCOMSTR") -ShCAction = SCons.Action.Action("$SHCCCOM", "$SHCCCOMSTR") -CXXAction = SCons.Action.Action("$CXXCOM", "$CXXCOMSTR") -ShCXXAction = SCons.Action.Action("$SHCXXCOM", "$SHCXXCOMSTR") - -ASAction = SCons.Action.Action("$ASCOM", "$ASCOMSTR") -ASPPAction = SCons.Action.Action("$ASPPCOM", "$ASPPCOMSTR") - -LinkAction = SCons.Action.Action("$LINKCOM", "$LINKCOMSTR") -ShLinkAction = SCons.Action.Action("$SHLINKCOM", "$SHLINKCOMSTR") - -LdModuleLinkAction = SCons.Action.Action("$LDMODULECOM", "$LDMODULECOMSTR") - -# Common tasks that we allow users to perform in platform-independent -# ways by creating ActionFactory instances. -ActionFactory = SCons.Action.ActionFactory - -def get_paths_str(dest): - # If dest is a list, we need to manually call str() on each element - if SCons.Util.is_List(dest): - elem_strs = [] - for element in dest: - elem_strs.append('"' + str(element) + '"') - return '[' + ', '.join(elem_strs) + ']' - else: - return '"' + str(dest) + '"' - -def chmod_func(dest, mode): - SCons.Node.FS.invalidate_node_memos(dest) - if not SCons.Util.is_List(dest): - dest = [dest] - for element in dest: - os.chmod(str(element), mode) - -def chmod_strfunc(dest, mode): - return 'Chmod(%s, 0%o)' % (get_paths_str(dest), mode) - -Chmod = ActionFactory(chmod_func, chmod_strfunc) - -def copy_func(dest, src): - SCons.Node.FS.invalidate_node_memos(dest) - if SCons.Util.is_List(src) and os.path.isdir(dest): - for file in src: - shutil.copy2(file, dest) - return 0 - elif os.path.isfile(src): - return shutil.copy2(src, dest) - else: - return shutil.copytree(src, dest, 1) - -Copy = ActionFactory(copy_func, - lambda dest, src: 'Copy("%s", "%s")' % (dest, src), - convert=str) - -def delete_func(dest, must_exist=0): - SCons.Node.FS.invalidate_node_memos(dest) - if not SCons.Util.is_List(dest): - dest = [dest] - for entry in dest: - entry = str(entry) - # os.path.exists returns False with broken links that exist - entry_exists = os.path.exists(entry) or os.path.islink(entry) - if not entry_exists and not must_exist: - continue - # os.path.isdir returns True when entry is a link to a dir - if os.path.isdir(entry) and not os.path.islink(entry): - shutil.rmtree(entry, 1) - continue - os.unlink(entry) - -def delete_strfunc(dest, must_exist=0): - return 'Delete(%s)' % get_paths_str(dest) - -Delete = ActionFactory(delete_func, delete_strfunc) - -def mkdir_func(dest): - SCons.Node.FS.invalidate_node_memos(dest) - if not SCons.Util.is_List(dest): - dest = [dest] - for entry in dest: - try: - os.makedirs(str(entry)) - except os.error, e: - p = str(entry) - if (e.args[0] == errno.EEXIST or - (sys.platform=='win32' and e.args[0]==183)) \ - and os.path.isdir(str(entry)): - pass # not an error if already exists - else: - raise - -Mkdir = ActionFactory(mkdir_func, - lambda dir: 'Mkdir(%s)' % get_paths_str(dir)) - -def move_func(dest, src): - SCons.Node.FS.invalidate_node_memos(dest) - SCons.Node.FS.invalidate_node_memos(src) - shutil.move(src, dest) - -Move = ActionFactory(move_func, - lambda dest, src: 'Move("%s", "%s")' % (dest, src), - convert=str) - -def touch_func(dest): - SCons.Node.FS.invalidate_node_memos(dest) - if not SCons.Util.is_List(dest): - dest = [dest] - for file in dest: - file = str(file) - mtime = int(time.time()) - if os.path.exists(file): - atime = os.path.getatime(file) - else: - open(file, 'w') - atime = mtime - os.utime(file, (atime, mtime)) - -Touch = ActionFactory(touch_func, - lambda file: 'Touch(%s)' % get_paths_str(file)) - -# Internal utility functions - -def _concat(prefix, list, suffix, env, f=lambda x: x, target=None, source=None): - """ - Creates a new list from 'list' by first interpolating each element - in the list using the 'env' dictionary and then calling f on the - list, and finally calling _concat_ixes to concatenate 'prefix' and - 'suffix' onto each element of the list. - """ - if not list: - return list - - l = f(SCons.PathList.PathList(list).subst_path(env, target, source)) - if l is not None: - list = l - - return _concat_ixes(prefix, list, suffix, env) - -def _concat_ixes(prefix, list, suffix, env): - """ - Creates a new list from 'list' by concatenating the 'prefix' and - 'suffix' arguments onto each element of the list. A trailing space - on 'prefix' or leading space on 'suffix' will cause them to be put - into separate list elements rather than being concatenated. - """ - - result = [] - - # ensure that prefix and suffix are strings - prefix = str(env.subst(prefix, SCons.Subst.SUBST_RAW)) - suffix = str(env.subst(suffix, SCons.Subst.SUBST_RAW)) - - for x in list: - if isinstance(x, SCons.Node.FS.File): - result.append(x) - continue - x = str(x) - if x: - - if prefix: - if prefix[-1] == ' ': - result.append(prefix[:-1]) - elif x[:len(prefix)] != prefix: - x = prefix + x - - result.append(x) - - if suffix: - if suffix[0] == ' ': - result.append(suffix[1:]) - elif x[-len(suffix):] != suffix: - result[-1] = result[-1]+suffix - - return result - -def _stripixes(prefix, itms, suffix, stripprefixes, stripsuffixes, env, c=None): - """ - This is a wrapper around _concat()/_concat_ixes() that checks for - the existence of prefixes or suffixes on list items and strips them - where it finds them. This is used by tools (like the GNU linker) - that need to turn something like 'libfoo.a' into '-lfoo'. - """ - - if not itms: - return itms - - if not callable(c): - env_c = env['_concat'] - if env_c != _concat and callable(env_c): - # There's a custom _concat() method in the construction - # environment, and we've allowed people to set that in - # the past (see test/custom-concat.py), so preserve the - # backwards compatibility. - c = env_c - else: - c = _concat_ixes - - stripprefixes = list(map(env.subst, SCons.Util.flatten(stripprefixes))) - stripsuffixes = list(map(env.subst, SCons.Util.flatten(stripsuffixes))) - - stripped = [] - for l in SCons.PathList.PathList(itms).subst_path(env, None, None): - if isinstance(l, SCons.Node.FS.File): - stripped.append(l) - continue - - if not SCons.Util.is_String(l): - l = str(l) - - for stripprefix in stripprefixes: - lsp = len(stripprefix) - if l[:lsp] == stripprefix: - l = l[lsp:] - # Do not strip more than one prefix - break - - for stripsuffix in stripsuffixes: - lss = len(stripsuffix) - if l[-lss:] == stripsuffix: - l = l[:-lss] - # Do not strip more than one suffix - break - - stripped.append(l) - - return c(prefix, stripped, suffix, env) - -def processDefines(defs): - """process defines, resolving strings, lists, dictionaries, into a list of - strings - """ - if SCons.Util.is_List(defs): - l = [] - for d in defs: - if d is None: - continue - elif SCons.Util.is_List(d) or isinstance(d, tuple): - if len(d) >= 2: - l.append(str(d[0]) + '=' + str(d[1])) - else: - l.append(str(d[0])) - elif SCons.Util.is_Dict(d): - for macro,value in d.iteritems(): - if value is not None: - l.append(str(macro) + '=' + str(value)) - else: - l.append(str(macro)) - elif SCons.Util.is_String(d): - l.append(str(d)) - else: - raise SCons.Errors.UserError("DEFINE %s is not a list, dict, string or None."%repr(d)) - elif SCons.Util.is_Dict(defs): - # The items in a dictionary are stored in random order, but - # if the order of the command-line options changes from - # invocation to invocation, then the signature of the command - # line will change and we'll get random unnecessary rebuilds. - # Consequently, we have to sort the keys to ensure a - # consistent order... - l = [] - for k,v in sorted(defs.items()): - if v is None: - l.append(str(k)) - else: - l.append(str(k) + '=' + str(v)) - else: - l = [str(defs)] - return l - -def _defines(prefix, defs, suffix, env, c=_concat_ixes): - """A wrapper around _concat_ixes that turns a list or string - into a list of C preprocessor command-line definitions. - """ - - return c(prefix, env.subst_path(processDefines(defs)), suffix, env) - -class NullCmdGenerator(object): - """This is a callable class that can be used in place of other - command generators if you don't want them to do anything. - - The __call__ method for this class simply returns the thing - you instantiated it with. - - Example usage: - env["DO_NOTHING"] = NullCmdGenerator - env["LINKCOM"] = "${DO_NOTHING('$LINK $SOURCES $TARGET')}" - """ - - def __init__(self, cmd): - self.cmd = cmd - - def __call__(self, target, source, env, for_signature=None): - return self.cmd - -class Variable_Method_Caller(object): - """A class for finding a construction variable on the stack and - calling one of its methods. - - We use this to support "construction variables" in our string - eval()s that actually stand in for methods--specifically, use - of "RDirs" in call to _concat that should actually execute the - "TARGET.RDirs" method. (We used to support this by creating a little - "build dictionary" that mapped RDirs to the method, but this got in - the way of Memoizing construction environments, because we had to - create new environment objects to hold the variables.) - """ - def __init__(self, variable, method): - self.variable = variable - self.method = method - def __call__(self, *args, **kw): - try: 1//0 - except ZeroDivisionError: - # Don't start iterating with the current stack-frame to - # prevent creating reference cycles (f_back is safe). - frame = sys.exc_info()[2].tb_frame.f_back - variable = self.variable - while frame: - if variable in frame.f_locals: - v = frame.f_locals[variable] - if v: - method = getattr(v, self.method) - return method(*args, **kw) - frame = frame.f_back - return None - -ConstructionEnvironment = { - 'BUILDERS' : {}, - 'SCANNERS' : [], - 'CONFIGUREDIR' : '#/.sconf_temp', - 'CONFIGURELOG' : '#/config.log', - 'CPPSUFFIXES' : SCons.Tool.CSuffixes, - 'DSUFFIXES' : SCons.Tool.DSuffixes, - 'ENV' : {}, - 'IDLSUFFIXES' : SCons.Tool.IDLSuffixes, -# 'LATEXSUFFIXES' : SCons.Tool.LaTeXSuffixes, # moved to the TeX tools generate functions - '_concat' : _concat, - '_defines' : _defines, - '_stripixes' : _stripixes, - '_LIBFLAGS' : '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}', - '_LIBDIRFLAGS' : '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', - '_CPPINCFLAGS' : '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', - '_CPPDEFFLAGS' : '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}', - 'TEMPFILE' : NullCmdGenerator, - 'Dir' : Variable_Method_Caller('TARGET', 'Dir'), - 'Dirs' : Variable_Method_Caller('TARGET', 'Dirs'), - 'File' : Variable_Method_Caller('TARGET', 'File'), - 'RDirs' : Variable_Method_Caller('TARGET', 'RDirs'), -} - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Environment.py b/scons/scons-local-2.2.0/SCons/Environment.py deleted file mode 100644 index addf782a3..000000000 --- a/scons/scons-local-2.2.0/SCons/Environment.py +++ /dev/null @@ -1,2417 +0,0 @@ -"""SCons.Environment - -Base class for construction Environments. These are -the primary objects used to communicate dependency and -construction information to the build engine. - -Keyword arguments supplied when the construction Environment -is created are construction variables used to initialize the -Environment -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Environment.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - - -import copy -import os -import sys -import re -import shlex -from collections import UserDict - -import SCons.Action -import SCons.Builder -from SCons.Debug import logInstanceCreation -import SCons.Defaults -import SCons.Errors -import SCons.Memoize -import SCons.Node -import SCons.Node.Alias -import SCons.Node.FS -import SCons.Node.Python -import SCons.Platform -import SCons.SConf -import SCons.SConsign -import SCons.Subst -import SCons.Tool -import SCons.Util -import SCons.Warnings - -class _Null(object): - pass - -_null = _Null - -_warn_copy_deprecated = True -_warn_source_signatures_deprecated = True -_warn_target_signatures_deprecated = True - -CleanTargets = {} -CalculatorArgs = {} - -semi_deepcopy = SCons.Util.semi_deepcopy -semi_deepcopy_dict = SCons.Util.semi_deepcopy_dict - -# Pull UserError into the global name space for the benefit of -# Environment().SourceSignatures(), which has some import statements -# which seem to mess up its ability to reference SCons directly. -UserError = SCons.Errors.UserError - -def alias_builder(env, target, source): - pass - -AliasBuilder = SCons.Builder.Builder(action = alias_builder, - target_factory = SCons.Node.Alias.default_ans.Alias, - source_factory = SCons.Node.FS.Entry, - multi = 1, - is_explicit = None, - name='AliasBuilder') - -def apply_tools(env, tools, toolpath): - # Store the toolpath in the Environment. - if toolpath is not None: - env['toolpath'] = toolpath - - if not tools: - return - # Filter out null tools from the list. - for tool in [_f for _f in tools if _f]: - if SCons.Util.is_List(tool) or isinstance(tool, tuple): - toolname = tool[0] - toolargs = tool[1] # should be a dict of kw args - tool = env.Tool(toolname, **toolargs) - else: - env.Tool(tool) - -# These names are (or will be) controlled by SCons; users should never -# set or override them. This warning can optionally be turned off, -# but scons will still ignore the illegal variable names even if it's off. -reserved_construction_var_names = [ - 'CHANGED_SOURCES', - 'CHANGED_TARGETS', - 'SOURCE', - 'SOURCES', - 'TARGET', - 'TARGETS', - 'UNCHANGED_SOURCES', - 'UNCHANGED_TARGETS', -] - -future_reserved_construction_var_names = [ - #'HOST_OS', - #'HOST_ARCH', - #'HOST_CPU', - ] - -def copy_non_reserved_keywords(dict): - result = semi_deepcopy(dict) - for k in result.keys(): - if k in reserved_construction_var_names: - msg = "Ignoring attempt to set reserved variable `$%s'" - SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg % k) - del result[k] - return result - -def _set_reserved(env, key, value): - msg = "Ignoring attempt to set reserved variable `$%s'" - SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg % key) - -def _set_future_reserved(env, key, value): - env._dict[key] = value - msg = "`$%s' will be reserved in a future release and setting it will become ignored" - SCons.Warnings.warn(SCons.Warnings.FutureReservedVariableWarning, msg % key) - -def _set_BUILDERS(env, key, value): - try: - bd = env._dict[key] - for k in bd.keys(): - del bd[k] - except KeyError: - bd = BuilderDict(kwbd, env) - env._dict[key] = bd - for k, v in value.items(): - if not SCons.Builder.is_a_Builder(v): - raise SCons.Errors.UserError('%s is not a Builder.' % repr(v)) - bd.update(value) - -def _del_SCANNERS(env, key): - del env._dict[key] - env.scanner_map_delete() - -def _set_SCANNERS(env, key, value): - env._dict[key] = value - env.scanner_map_delete() - -def _delete_duplicates(l, keep_last): - """Delete duplicates from a sequence, keeping the first or last.""" - seen={} - result=[] - if keep_last: # reverse in & out, then keep first - l.reverse() - for i in l: - try: - if i not in seen: - result.append(i) - seen[i]=1 - except TypeError: - # probably unhashable. Just keep it. - result.append(i) - if keep_last: - result.reverse() - return result - - - -# The following is partly based on code in a comment added by Peter -# Shannon at the following page (there called the "transplant" class): -# -# ASPN : Python Cookbook : Dynamically added methods to a class -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732 -# -# We had independently been using the idiom as BuilderWrapper, but -# factoring out the common parts into this base class, and making -# BuilderWrapper a subclass that overrides __call__() to enforce specific -# Builder calling conventions, simplified some of our higher-layer code. - -class MethodWrapper(object): - """ - A generic Wrapper class that associates a method (which can - actually be any callable) with an object. As part of creating this - MethodWrapper object an attribute with the specified (by default, - the name of the supplied method) is added to the underlying object. - When that new "method" is called, our __call__() method adds the - object as the first argument, simulating the Python behavior of - supplying "self" on method calls. - - We hang on to the name by which the method was added to the underlying - base class so that we can provide a method to "clone" ourselves onto - a new underlying object being copied (without which we wouldn't need - to save that info). - """ - def __init__(self, object, method, name=None): - if name is None: - name = method.__name__ - self.object = object - self.method = method - self.name = name - setattr(self.object, name, self) - - def __call__(self, *args, **kwargs): - nargs = (self.object,) + args - return self.method(*nargs, **kwargs) - - def clone(self, new_object): - """ - Returns an object that re-binds the underlying "method" to - the specified new object. - """ - return self.__class__(new_object, self.method, self.name) - -class BuilderWrapper(MethodWrapper): - """ - A MethodWrapper subclass that that associates an environment with - a Builder. - - This mainly exists to wrap the __call__() function so that all calls - to Builders can have their argument lists massaged in the same way - (treat a lone argument as the source, treat two arguments as target - then source, make sure both target and source are lists) without - having to have cut-and-paste code to do it. - - As a bit of obsessive backwards compatibility, we also intercept - attempts to get or set the "env" or "builder" attributes, which were - the names we used before we put the common functionality into the - MethodWrapper base class. We'll keep this around for a while in case - people shipped Tool modules that reached into the wrapper (like the - Tool/qt.py module does, or did). There shouldn't be a lot attribute - fetching or setting on these, so a little extra work shouldn't hurt. - """ - def __call__(self, target=None, source=_null, *args, **kw): - if source is _null: - source = target - target = None - if target is not None and not SCons.Util.is_List(target): - target = [target] - if source is not None and not SCons.Util.is_List(source): - source = [source] - return MethodWrapper.__call__(self, target, source, *args, **kw) - - def __repr__(self): - return '' % repr(self.name) - - def __str__(self): - return self.__repr__() - - def __getattr__(self, name): - if name == 'env': - return self.object - elif name == 'builder': - return self.method - else: - raise AttributeError(name) - - def __setattr__(self, name, value): - if name == 'env': - self.object = value - elif name == 'builder': - self.method = value - else: - self.__dict__[name] = value - - # This allows a Builder to be executed directly - # through the Environment to which it's attached. - # In practice, we shouldn't need this, because - # builders actually get executed through a Node. - # But we do have a unit test for this, and can't - # yet rule out that it would be useful in the - # future, so leave it for now. - #def execute(self, **kw): - # kw['env'] = self.env - # self.builder.execute(**kw) - -class BuilderDict(UserDict): - """This is a dictionary-like class used by an Environment to hold - the Builders. We need to do this because every time someone changes - the Builders in the Environment's BUILDERS dictionary, we must - update the Environment's attributes.""" - def __init__(self, dict, env): - # Set self.env before calling the superclass initialization, - # because it will end up calling our other methods, which will - # need to point the values in this dictionary to self.env. - self.env = env - UserDict.__init__(self, dict) - - def __semi_deepcopy__(self): - # These cannot be copied since they would both modify the same builder object, and indeed - # just copying would modify the original builder - raise TypeError( 'cannot semi_deepcopy a BuilderDict' ) - - def __setitem__(self, item, val): - try: - method = getattr(self.env, item).method - except AttributeError: - pass - else: - self.env.RemoveMethod(method) - UserDict.__setitem__(self, item, val) - BuilderWrapper(self.env, val, item) - - def __delitem__(self, item): - UserDict.__delitem__(self, item) - delattr(self.env, item) - - def update(self, dict): - for i, v in dict.items(): - self.__setitem__(i, v) - - - -_is_valid_var = re.compile(r'[_a-zA-Z]\w*$') - -def is_valid_construction_var(varstr): - """Return if the specified string is a legitimate construction - variable. - """ - return _is_valid_var.match(varstr) - - - -class SubstitutionEnvironment(object): - """Base class for different flavors of construction environments. - - This class contains a minimal set of methods that handle contruction - variable expansion and conversion of strings to Nodes, which may or - may not be actually useful as a stand-alone class. Which methods - ended up in this class is pretty arbitrary right now. They're - basically the ones which we've empirically determined are common to - the different construction environment subclasses, and most of the - others that use or touch the underlying dictionary of construction - variables. - - Eventually, this class should contain all the methods that we - determine are necessary for a "minimal" interface to the build engine. - A full "native Python" SCons environment has gotten pretty heavyweight - with all of the methods and Tools and construction variables we've - jammed in there, so it would be nice to have a lighter weight - alternative for interfaces that don't need all of the bells and - whistles. (At some point, we'll also probably rename this class - "Base," since that more reflects what we want this class to become, - but because we've released comments that tell people to subclass - Environment.Base to create their own flavors of construction - environment, we'll save that for a future refactoring when this - class actually becomes useful.) - """ - - if SCons.Memoize.use_memoizer: - __metaclass__ = SCons.Memoize.Memoized_Metaclass - - def __init__(self, **kw): - """Initialization of an underlying SubstitutionEnvironment class. - """ - if __debug__: logInstanceCreation(self, 'Environment.SubstitutionEnvironment') - self.fs = SCons.Node.FS.get_default_fs() - self.ans = SCons.Node.Alias.default_ans - self.lookup_list = SCons.Node.arg2nodes_lookups - self._dict = kw.copy() - self._init_special() - self.added_methods = [] - #self._memo = {} - - def _init_special(self): - """Initial the dispatch tables for special handling of - special construction variables.""" - self._special_del = {} - self._special_del['SCANNERS'] = _del_SCANNERS - - self._special_set = {} - for key in reserved_construction_var_names: - self._special_set[key] = _set_reserved - for key in future_reserved_construction_var_names: - self._special_set[key] = _set_future_reserved - self._special_set['BUILDERS'] = _set_BUILDERS - self._special_set['SCANNERS'] = _set_SCANNERS - - # Freeze the keys of self._special_set in a list for use by - # methods that need to check. (Empirically, list scanning has - # gotten better than dict.has_key() in Python 2.5.) - self._special_set_keys = list(self._special_set.keys()) - - def __cmp__(self, other): - return cmp(self._dict, other._dict) - - def __delitem__(self, key): - special = self._special_del.get(key) - if special: - special(self, key) - else: - del self._dict[key] - - def __getitem__(self, key): - return self._dict[key] - - def __setitem__(self, key, value): - # This is heavily used. This implementation is the best we have - # according to the timings in bench/env.__setitem__.py. - # - # The "key in self._special_set_keys" test here seems to perform - # pretty well for the number of keys we have. A hard-coded - # list works a little better in Python 2.5, but that has the - # disadvantage of maybe getting out of sync if we ever add more - # variable names. Using self._special_set.has_key() works a - # little better in Python 2.4, but is worse than this test. - # So right now it seems like a good trade-off, but feel free to - # revisit this with bench/env.__setitem__.py as needed (and - # as newer versions of Python come out). - if key in self._special_set_keys: - self._special_set[key](self, key, value) - else: - # If we already have the entry, then it's obviously a valid - # key and we don't need to check. If we do check, using a - # global, pre-compiled regular expression directly is more - # efficient than calling another function or a method. - if key not in self._dict \ - and not _is_valid_var.match(key): - raise SCons.Errors.UserError("Illegal construction variable `%s'" % key) - self._dict[key] = value - - def get(self, key, default=None): - """Emulates the get() method of dictionaries.""" - return self._dict.get(key, default) - - def has_key(self, key): - return key in self._dict - - def __contains__(self, key): - return self._dict.__contains__(key) - - def items(self): - return list(self._dict.items()) - - def arg2nodes(self, args, node_factory=_null, lookup_list=_null, **kw): - if node_factory is _null: - node_factory = self.fs.File - if lookup_list is _null: - lookup_list = self.lookup_list - - if not args: - return [] - - args = SCons.Util.flatten(args) - - nodes = [] - for v in args: - if SCons.Util.is_String(v): - n = None - for l in lookup_list: - n = l(v) - if n is not None: - break - if n is not None: - if SCons.Util.is_String(n): - # n = self.subst(n, raw=1, **kw) - kw['raw'] = 1 - n = self.subst(n, **kw) - if node_factory: - n = node_factory(n) - if SCons.Util.is_List(n): - nodes.extend(n) - else: - nodes.append(n) - elif node_factory: - # v = node_factory(self.subst(v, raw=1, **kw)) - kw['raw'] = 1 - v = node_factory(self.subst(v, **kw)) - if SCons.Util.is_List(v): - nodes.extend(v) - else: - nodes.append(v) - else: - nodes.append(v) - - return nodes - - def gvars(self): - return self._dict - - def lvars(self): - return {} - - def subst(self, string, raw=0, target=None, source=None, conv=None, executor=None): - """Recursively interpolates construction variables from the - Environment into the specified string, returning the expanded - result. Construction variables are specified by a $ prefix - in the string and begin with an initial underscore or - alphabetic character followed by any number of underscores - or alphanumeric characters. The construction variable names - may be surrounded by curly braces to separate the name from - trailing characters. - """ - gvars = self.gvars() - lvars = self.lvars() - lvars['__env__'] = self - if executor: - lvars.update(executor.get_lvars()) - return SCons.Subst.scons_subst(string, self, raw, target, source, gvars, lvars, conv) - - def subst_kw(self, kw, raw=0, target=None, source=None): - nkw = {} - for k, v in kw.items(): - k = self.subst(k, raw, target, source) - if SCons.Util.is_String(v): - v = self.subst(v, raw, target, source) - nkw[k] = v - return nkw - - def subst_list(self, string, raw=0, target=None, source=None, conv=None, executor=None): - """Calls through to SCons.Subst.scons_subst_list(). See - the documentation for that function.""" - gvars = self.gvars() - lvars = self.lvars() - lvars['__env__'] = self - if executor: - lvars.update(executor.get_lvars()) - return SCons.Subst.scons_subst_list(string, self, raw, target, source, gvars, lvars, conv) - - def subst_path(self, path, target=None, source=None): - """Substitute a path list, turning EntryProxies into Nodes - and leaving Nodes (and other objects) as-is.""" - - if not SCons.Util.is_List(path): - path = [path] - - def s(obj): - """This is the "string conversion" routine that we have our - substitutions use to return Nodes, not strings. This relies - on the fact that an EntryProxy object has a get() method that - returns the underlying Node that it wraps, which is a bit of - architectural dependence that we might need to break or modify - in the future in response to additional requirements.""" - try: - get = obj.get - except AttributeError: - obj = SCons.Util.to_String_for_subst(obj) - else: - obj = get() - return obj - - r = [] - for p in path: - if SCons.Util.is_String(p): - p = self.subst(p, target=target, source=source, conv=s) - if SCons.Util.is_List(p): - if len(p) == 1: - p = p[0] - else: - # We have an object plus a string, or multiple - # objects that we need to smush together. No choice - # but to make them into a string. - p = ''.join(map(SCons.Util.to_String_for_subst, p)) - else: - p = s(p) - r.append(p) - return r - - subst_target_source = subst - - def backtick(self, command): - import subprocess - # common arguments - kw = { 'stdin' : 'devnull', - 'stdout' : subprocess.PIPE, - 'stderr' : subprocess.PIPE, - 'universal_newlines' : True, - } - # if the command is a list, assume it's been quoted - # othewise force a shell - if not SCons.Util.is_List(command): kw['shell'] = True - # run constructed command - p = SCons.Action._subproc(self, command, **kw) - out,err = p.communicate() - status = p.wait() - if err: - sys.stderr.write(unicode(err)) - if status: - raise OSError("'%s' exited %d" % (command, status)) - return out - - def AddMethod(self, function, name=None): - """ - Adds the specified function as a method of this construction - environment with the specified name. If the name is omitted, - the default name is the name of the function itself. - """ - method = MethodWrapper(self, function, name) - self.added_methods.append(method) - - def RemoveMethod(self, function): - """ - Removes the specified function's MethodWrapper from the - added_methods list, so we don't re-bind it when making a clone. - """ - self.added_methods = [dm for dm in self.added_methods if not dm.method is function] - - def Override(self, overrides): - """ - Produce a modified environment whose variables are overriden by - the overrides dictionaries. "overrides" is a dictionary that - will override the variables of this environment. - - This function is much more efficient than Clone() or creating - a new Environment because it doesn't copy the construction - environment dictionary, it just wraps the underlying construction - environment, and doesn't even create a wrapper object if there - are no overrides. - """ - if not overrides: return self - o = copy_non_reserved_keywords(overrides) - if not o: return self - overrides = {} - merges = None - for key, value in o.items(): - if key == 'parse_flags': - merges = value - else: - overrides[key] = SCons.Subst.scons_subst_once(value, self, key) - env = OverrideEnvironment(self, overrides) - if merges: env.MergeFlags(merges) - return env - - def ParseFlags(self, *flags): - """ - Parse the set of flags and return a dict with the flags placed - in the appropriate entry. The flags are treated as a typical - set of command-line flags for a GNU-like toolchain and used to - populate the entries in the dict immediately below. If one of - the flag strings begins with a bang (exclamation mark), it is - assumed to be a command and the rest of the string is executed; - the result of that evaluation is then added to the dict. - """ - dict = { - 'ASFLAGS' : SCons.Util.CLVar(''), - 'CFLAGS' : SCons.Util.CLVar(''), - 'CCFLAGS' : SCons.Util.CLVar(''), - 'CXXFLAGS' : SCons.Util.CLVar(''), - 'CPPDEFINES' : [], - 'CPPFLAGS' : SCons.Util.CLVar(''), - 'CPPPATH' : [], - 'FRAMEWORKPATH' : SCons.Util.CLVar(''), - 'FRAMEWORKS' : SCons.Util.CLVar(''), - 'LIBPATH' : [], - 'LIBS' : [], - 'LINKFLAGS' : SCons.Util.CLVar(''), - 'RPATH' : [], - } - - def do_parse(arg): - # if arg is a sequence, recurse with each element - if not arg: - return - - if not SCons.Util.is_String(arg): - for t in arg: do_parse(t) - return - - # if arg is a command, execute it - if arg[0] == '!': - arg = self.backtick(arg[1:]) - - # utility function to deal with -D option - def append_define(name, dict = dict): - t = name.split('=') - if len(t) == 1: - dict['CPPDEFINES'].append(name) - else: - dict['CPPDEFINES'].append([t[0], '='.join(t[1:])]) - - # Loop through the flags and add them to the appropriate option. - # This tries to strike a balance between checking for all possible - # flags and keeping the logic to a finite size, so it doesn't - # check for some that don't occur often. It particular, if the - # flag is not known to occur in a config script and there's a way - # of passing the flag to the right place (by wrapping it in a -W - # flag, for example) we don't check for it. Note that most - # preprocessor options are not handled, since unhandled options - # are placed in CCFLAGS, so unless the preprocessor is invoked - # separately, these flags will still get to the preprocessor. - # Other options not currently handled: - # -iqoutedir (preprocessor search path) - # -u symbol (linker undefined symbol) - # -s (linker strip files) - # -static* (linker static binding) - # -shared* (linker dynamic binding) - # -symbolic (linker global binding) - # -R dir (deprecated linker rpath) - # IBM compilers may also accept -qframeworkdir=foo - - params = shlex.split(arg) - append_next_arg_to = None # for multi-word args - for arg in params: - if append_next_arg_to: - if append_next_arg_to == 'CPPDEFINES': - append_define(arg) - elif append_next_arg_to == '-include': - t = ('-include', self.fs.File(arg)) - dict['CCFLAGS'].append(t) - elif append_next_arg_to == '-isysroot': - t = ('-isysroot', arg) - dict['CCFLAGS'].append(t) - dict['LINKFLAGS'].append(t) - elif append_next_arg_to == '-arch': - t = ('-arch', arg) - dict['CCFLAGS'].append(t) - dict['LINKFLAGS'].append(t) - else: - dict[append_next_arg_to].append(arg) - append_next_arg_to = None - elif not arg[0] in ['-', '+']: - dict['LIBS'].append(self.fs.File(arg)) - elif arg == '-dylib_file': - dict['LINKFLAGS'].append(arg) - append_next_arg_to = 'LINKFLAGS' - elif arg[:2] == '-L': - if arg[2:]: - dict['LIBPATH'].append(arg[2:]) - else: - append_next_arg_to = 'LIBPATH' - elif arg[:2] == '-l': - if arg[2:]: - dict['LIBS'].append(arg[2:]) - else: - append_next_arg_to = 'LIBS' - elif arg[:2] == '-I': - if arg[2:]: - dict['CPPPATH'].append(arg[2:]) - else: - append_next_arg_to = 'CPPPATH' - elif arg[:4] == '-Wa,': - dict['ASFLAGS'].append(arg[4:]) - dict['CCFLAGS'].append(arg) - elif arg[:4] == '-Wl,': - if arg[:11] == '-Wl,-rpath=': - dict['RPATH'].append(arg[11:]) - elif arg[:7] == '-Wl,-R,': - dict['RPATH'].append(arg[7:]) - elif arg[:6] == '-Wl,-R': - dict['RPATH'].append(arg[6:]) - else: - dict['LINKFLAGS'].append(arg) - elif arg[:4] == '-Wp,': - dict['CPPFLAGS'].append(arg) - elif arg[:2] == '-D': - if arg[2:]: - append_define(arg[2:]) - else: - append_next_arg_to = 'CPPDEFINES' - elif arg == '-framework': - append_next_arg_to = 'FRAMEWORKS' - elif arg[:14] == '-frameworkdir=': - dict['FRAMEWORKPATH'].append(arg[14:]) - elif arg[:2] == '-F': - if arg[2:]: - dict['FRAMEWORKPATH'].append(arg[2:]) - else: - append_next_arg_to = 'FRAMEWORKPATH' - elif arg in ['-mno-cygwin', - '-pthread', - '-openmp', - '-fopenmp']: - dict['CCFLAGS'].append(arg) - dict['LINKFLAGS'].append(arg) - elif arg == '-mwindows': - dict['LINKFLAGS'].append(arg) - elif arg[:5] == '-std=': - if arg[5:].find('++')!=-1: - key='CXXFLAGS' - else: - key='CFLAGS' - dict[key].append(arg) - elif arg[0] == '+': - dict['CCFLAGS'].append(arg) - dict['LINKFLAGS'].append(arg) - elif arg in ['-include', '-isysroot', '-arch']: - append_next_arg_to = arg - else: - dict['CCFLAGS'].append(arg) - - for arg in flags: - do_parse(arg) - return dict - - def MergeFlags(self, args, unique=1, dict=None): - """ - Merge the dict in args into the construction variables of this - env, or the passed-in dict. If args is not a dict, it is - converted into a dict using ParseFlags. If unique is not set, - the flags are appended rather than merged. - """ - - if dict is None: - dict = self - if not SCons.Util.is_Dict(args): - args = self.ParseFlags(args) - if not unique: - self.Append(**args) - return self - for key, value in args.items(): - if not value: - continue - try: - orig = self[key] - except KeyError: - orig = value - else: - if not orig: - orig = value - elif value: - # Add orig and value. The logic here was lifted from - # part of env.Append() (see there for a lot of comments - # about the order in which things are tried) and is - # used mainly to handle coercion of strings to CLVar to - # "do the right thing" given (e.g.) an original CCFLAGS - # string variable like '-pipe -Wall'. - try: - orig = orig + value - except (KeyError, TypeError): - try: - add_to_orig = orig.append - except AttributeError: - value.insert(0, orig) - orig = value - else: - add_to_orig(value) - t = [] - if key[-4:] == 'PATH': - ### keep left-most occurence - for v in orig: - if v not in t: - t.append(v) - else: - ### keep right-most occurence - orig.reverse() - for v in orig: - if v not in t: - t.insert(0, v) - self[key] = t - return self - -# def MergeShellPaths(self, args, prepend=1): -# """ -# Merge the dict in args into the shell environment in env['ENV']. -# Shell path elements are appended or prepended according to prepend. - -# Uses Pre/AppendENVPath, so it always appends or prepends uniquely. - -# Example: env.MergeShellPaths({'LIBPATH': '/usr/local/lib'}) -# prepends /usr/local/lib to env['ENV']['LIBPATH']. -# """ - -# for pathname, pathval in args.items(): -# if not pathval: -# continue -# if prepend: -# self.PrependENVPath(pathname, pathval) -# else: -# self.AppendENVPath(pathname, pathval) - - -def default_decide_source(dependency, target, prev_ni): - f = SCons.Defaults.DefaultEnvironment().decide_source - return f(dependency, target, prev_ni) - -def default_decide_target(dependency, target, prev_ni): - f = SCons.Defaults.DefaultEnvironment().decide_target - return f(dependency, target, prev_ni) - -def default_copy_from_cache(src, dst): - f = SCons.Defaults.DefaultEnvironment().copy_from_cache - return f(src, dst) - -class Base(SubstitutionEnvironment): - """Base class for "real" construction Environments. These are the - primary objects used to communicate dependency and construction - information to the build engine. - - Keyword arguments supplied when the construction Environment - is created are construction variables used to initialize the - Environment. - """ - - memoizer_counters = [] - - ####################################################################### - # This is THE class for interacting with the SCons build engine, - # and it contains a lot of stuff, so we're going to try to keep this - # a little organized by grouping the methods. - ####################################################################### - - ####################################################################### - # Methods that make an Environment act like a dictionary. These have - # the expected standard names for Python mapping objects. Note that - # we don't actually make an Environment a subclass of UserDict for - # performance reasons. Note also that we only supply methods for - # dictionary functionality that we actually need and use. - ####################################################################### - - def __init__(self, - platform=None, - tools=None, - toolpath=None, - variables=None, - parse_flags = None, - **kw): - """ - Initialization of a basic SCons construction environment, - including setting up special construction variables like BUILDER, - PLATFORM, etc., and searching for and applying available Tools. - - Note that we do *not* call the underlying base class - (SubsitutionEnvironment) initialization, because we need to - initialize things in a very specific order that doesn't work - with the much simpler base class initialization. - """ - if __debug__: logInstanceCreation(self, 'Environment.Base') - self._memo = {} - self.fs = SCons.Node.FS.get_default_fs() - self.ans = SCons.Node.Alias.default_ans - self.lookup_list = SCons.Node.arg2nodes_lookups - self._dict = semi_deepcopy(SCons.Defaults.ConstructionEnvironment) - self._init_special() - self.added_methods = [] - - # We don't use AddMethod, or define these as methods in this - # class, because we *don't* want these functions to be bound - # methods. They need to operate independently so that the - # settings will work properly regardless of whether a given - # target ends up being built with a Base environment or an - # OverrideEnvironment or what have you. - self.decide_target = default_decide_target - self.decide_source = default_decide_source - - self.copy_from_cache = default_copy_from_cache - - self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self) - - if platform is None: - platform = self._dict.get('PLATFORM', None) - if platform is None: - platform = SCons.Platform.Platform() - if SCons.Util.is_String(platform): - platform = SCons.Platform.Platform(platform) - self._dict['PLATFORM'] = str(platform) - platform(self) - - self._dict['HOST_OS'] = self._dict.get('HOST_OS',None) - self._dict['HOST_ARCH'] = self._dict.get('HOST_ARCH',None) - - # Now set defaults for TARGET_{OS|ARCH} - self._dict['TARGET_OS'] = self._dict.get('HOST_OS',None) - self._dict['TARGET_ARCH'] = self._dict.get('HOST_ARCH',None) - - - # Apply the passed-in and customizable variables to the - # environment before calling the tools, because they may use - # some of them during initialization. - if 'options' in kw: - # Backwards compatibility: they may stll be using the - # old "options" keyword. - variables = kw['options'] - del kw['options'] - self.Replace(**kw) - keys = list(kw.keys()) - if variables: - keys = keys + list(variables.keys()) - variables.Update(self) - - save = {} - for k in keys: - try: - save[k] = self._dict[k] - except KeyError: - # No value may have been set if they tried to pass in a - # reserved variable name like TARGETS. - pass - - SCons.Tool.Initializers(self) - - if tools is None: - tools = self._dict.get('TOOLS', None) - if tools is None: - tools = ['default'] - apply_tools(self, tools, toolpath) - - # Now restore the passed-in and customized variables - # to the environment, since the values the user set explicitly - # should override any values set by the tools. - for key, val in save.items(): - self._dict[key] = val - - # Finally, apply any flags to be merged in - if parse_flags: self.MergeFlags(parse_flags) - - ####################################################################### - # Utility methods that are primarily for internal use by SCons. - # These begin with lower-case letters. - ####################################################################### - - def get_builder(self, name): - """Fetch the builder with the specified name from the environment. - """ - try: - return self._dict['BUILDERS'][name] - except KeyError: - return None - - def get_CacheDir(self): - try: - path = self._CacheDir_path - except AttributeError: - path = SCons.Defaults.DefaultEnvironment()._CacheDir_path - try: - if path == self._last_CacheDir_path: - return self._last_CacheDir - except AttributeError: - pass - cd = SCons.CacheDir.CacheDir(path) - self._last_CacheDir_path = path - self._last_CacheDir = cd - return cd - - def get_factory(self, factory, default='File'): - """Return a factory function for creating Nodes for this - construction environment. - """ - name = default - try: - is_node = issubclass(factory, SCons.Node.FS.Base) - except TypeError: - # The specified factory isn't a Node itself--it's - # most likely None, or possibly a callable. - pass - else: - if is_node: - # The specified factory is a Node (sub)class. Try to - # return the FS method that corresponds to the Node's - # name--that is, we return self.fs.Dir if they want a Dir, - # self.fs.File for a File, etc. - try: name = factory.__name__ - except AttributeError: pass - else: factory = None - if not factory: - # They passed us None, or we picked up a name from a specified - # class, so return the FS method. (Note that we *don't* - # use our own self.{Dir,File} methods because that would - # cause env.subst() to be called twice on the file name, - # interfering with files that have $$ in them.) - factory = getattr(self.fs, name) - return factory - - memoizer_counters.append(SCons.Memoize.CountValue('_gsm')) - - def _gsm(self): - try: - return self._memo['_gsm'] - except KeyError: - pass - - result = {} - - try: - scanners = self._dict['SCANNERS'] - except KeyError: - pass - else: - # Reverse the scanner list so that, if multiple scanners - # claim they can scan the same suffix, earlier scanners - # in the list will overwrite later scanners, so that - # the result looks like a "first match" to the user. - if not SCons.Util.is_List(scanners): - scanners = [scanners] - else: - scanners = scanners[:] # copy so reverse() doesn't mod original - scanners.reverse() - for scanner in scanners: - for k in scanner.get_skeys(self): - if k and self['PLATFORM'] == 'win32': - k = k.lower() - result[k] = scanner - - self._memo['_gsm'] = result - - return result - - def get_scanner(self, skey): - """Find the appropriate scanner given a key (usually a file suffix). - """ - if skey and self['PLATFORM'] == 'win32': - skey = skey.lower() - return self._gsm().get(skey) - - def scanner_map_delete(self, kw=None): - """Delete the cached scanner map (if we need to). - """ - try: - del self._memo['_gsm'] - except KeyError: - pass - - def _update(self, dict): - """Update an environment's values directly, bypassing the normal - checks that occur when users try to set items. - """ - self._dict.update(dict) - - def get_src_sig_type(self): - try: - return self.src_sig_type - except AttributeError: - t = SCons.Defaults.DefaultEnvironment().src_sig_type - self.src_sig_type = t - return t - - def get_tgt_sig_type(self): - try: - return self.tgt_sig_type - except AttributeError: - t = SCons.Defaults.DefaultEnvironment().tgt_sig_type - self.tgt_sig_type = t - return t - - ####################################################################### - # Public methods for manipulating an Environment. These begin with - # upper-case letters. The essential characteristic of methods in - # this section is that they do *not* have corresponding same-named - # global functions. For example, a stand-alone Append() function - # makes no sense, because Append() is all about appending values to - # an Environment's construction variables. - ####################################################################### - - def Append(self, **kw): - """Append values to existing construction variables - in an Environment. - """ - kw = copy_non_reserved_keywords(kw) - for key, val in kw.items(): - # It would be easier on the eyes to write this using - # "continue" statements whenever we finish processing an item, - # but Python 1.5.2 apparently doesn't let you use "continue" - # within try:-except: blocks, so we have to nest our code. - try: - if key == 'CPPDEFINES' and SCons.Util.is_String(self._dict[key]): - self._dict[key] = [self._dict[key]] - orig = self._dict[key] - except KeyError: - # No existing variable in the environment, so just set - # it to the new value. - if key == 'CPPDEFINES' and SCons.Util.is_String(val): - self._dict[key] = [val] - else: - self._dict[key] = val - else: - try: - # Check if the original looks like a dictionary. - # If it is, we can't just try adding the value because - # dictionaries don't have __add__() methods, and - # things like UserList will incorrectly coerce the - # original dict to a list (which we don't want). - update_dict = orig.update - except AttributeError: - try: - # Most straightforward: just try to add them - # together. This will work in most cases, when the - # original and new values are of compatible types. - self._dict[key] = orig + val - except (KeyError, TypeError): - try: - # Check if the original is a list. - add_to_orig = orig.append - except AttributeError: - # The original isn't a list, but the new - # value is (by process of elimination), - # so insert the original in the new value - # (if there's one to insert) and replace - # the variable with it. - if orig: - val.insert(0, orig) - self._dict[key] = val - else: - # The original is a list, so append the new - # value to it (if there's a value to append). - if val: - add_to_orig(val) - else: - # The original looks like a dictionary, so update it - # based on what we think the value looks like. - if SCons.Util.is_List(val): - if key == 'CPPDEFINES': - orig = orig.items() - orig += val - self._dict[key] = orig - else: - for v in val: - orig[v] = None - else: - try: - update_dict(val) - except (AttributeError, TypeError, ValueError): - if SCons.Util.is_Dict(val): - for k, v in val.items(): - orig[k] = v - else: - orig[val] = None - self.scanner_map_delete(kw) - - # allow Dirs and strings beginning with # for top-relative - # Note this uses the current env's fs (in self). - def _canonicalize(self, path): - if not SCons.Util.is_String(path): # typically a Dir - path = str(path) - if path and path[0] == '#': - path = str(self.fs.Dir(path)) - return path - - def AppendENVPath(self, name, newpath, envname = 'ENV', - sep = os.pathsep, delete_existing=1): - """Append path elements to the path 'name' in the 'ENV' - dictionary for this environment. Will only add any particular - path once, and will normpath and normcase all paths to help - assure this. This can also handle the case where the env - variable is a list instead of a string. - - If delete_existing is 0, a newpath which is already in the path - will not be moved to the end (it will be left where it is). - """ - - orig = '' - if envname in self._dict and name in self._dict[envname]: - orig = self._dict[envname][name] - - nv = SCons.Util.AppendPath(orig, newpath, sep, delete_existing, - canonicalize=self._canonicalize) - - if envname not in self._dict: - self._dict[envname] = {} - - self._dict[envname][name] = nv - - def AppendUnique(self, delete_existing=0, **kw): - """Append values to existing construction variables - in an Environment, if they're not already there. - If delete_existing is 1, removes existing values first, so - values move to end. - """ - kw = copy_non_reserved_keywords(kw) - for key, val in kw.items(): - if SCons.Util.is_List(val): - val = _delete_duplicates(val, delete_existing) - if key not in self._dict or self._dict[key] in ('', None): - self._dict[key] = val - elif SCons.Util.is_Dict(self._dict[key]) and \ - SCons.Util.is_Dict(val): - self._dict[key].update(val) - elif SCons.Util.is_List(val): - dk = self._dict[key] - if key == 'CPPDEFINES': - tmp = [] - for i in val: - if SCons.Util.is_List(i): - if len(i) >= 2: - tmp.append((i[0], i[1])) - else: - tmp.append((i[0],)) - elif SCons.Util.is_Tuple(i): - tmp.append(i) - else: - tmp.append((i,)) - val = tmp - if SCons.Util.is_Dict(dk): - dk = dk.items() - elif SCons.Util.is_String(dk): - dk = [(dk,)] - else: - tmp = [] - for i in dk: - if SCons.Util.is_List(i): - if len(i) >= 2: - tmp.append((i[0], i[1])) - else: - tmp.append((i[0],)) - elif SCons.Util.is_Tuple(i): - tmp.append(i) - else: - tmp.append((i,)) - dk = tmp - else: - if not SCons.Util.is_List(dk): - dk = [dk] - if delete_existing: - dk = [x for x in dk if x not in val] - else: - val = [x for x in val if x not in dk] - self._dict[key] = dk + val - else: - dk = self._dict[key] - if SCons.Util.is_List(dk): - if key == 'CPPDEFINES': - tmp = [] - for i in dk: - if SCons.Util.is_List(i): - if len(i) >= 2: - tmp.append((i[0], i[1])) - else: - tmp.append((i[0],)) - elif SCons.Util.is_Tuple(i): - tmp.append(i) - else: - tmp.append((i,)) - dk = tmp - if SCons.Util.is_Dict(val): - val = val.items() - elif SCons.Util.is_String(val): - val = [(val,)] - if delete_existing: - dk = filter(lambda x, val=val: x not in val, dk) - self._dict[key] = dk + val - else: - dk = [x for x in dk if x not in val] - self._dict[key] = dk + val - else: - # By elimination, val is not a list. Since dk is a - # list, wrap val in a list first. - if delete_existing: - dk = filter(lambda x, val=val: x not in val, dk) - self._dict[key] = dk + [val] - else: - if not val in dk: - self._dict[key] = dk + [val] - else: - if key == 'CPPDEFINES': - if SCons.Util.is_String(dk): - dk = [dk] - elif SCons.Util.is_Dict(dk): - dk = dk.items() - if SCons.Util.is_String(val): - if val in dk: - val = [] - else: - val = [val] - elif SCons.Util.is_Dict(val): - tmp = [] - for i,j in val.iteritems(): - if j is not None: - tmp.append((i,j)) - else: - tmp.append(i) - val = tmp - if delete_existing: - dk = [x for x in dk if x not in val] - self._dict[key] = dk + val - self.scanner_map_delete(kw) - - def Clone(self, tools=[], toolpath=None, parse_flags = None, **kw): - """Return a copy of a construction Environment. The - copy is like a Python "deep copy"--that is, independent - copies are made recursively of each objects--except that - a reference is copied when an object is not deep-copyable - (like a function). There are no references to any mutable - objects in the original Environment. - """ - try: - builders = self._dict['BUILDERS'] - except KeyError: - pass - - clone = copy.copy(self) - # BUILDERS is not safe to do a simple copy - clone._dict = semi_deepcopy_dict(self._dict, ['BUILDERS']) - clone._dict['BUILDERS'] = BuilderDict(builders, clone) - - # Check the methods added via AddMethod() and re-bind them to - # the cloned environment. Only do this if the attribute hasn't - # been overwritten by the user explicitly and still points to - # the added method. - clone.added_methods = [] - for mw in self.added_methods: - if mw == getattr(self, mw.name): - clone.added_methods.append(mw.clone(clone)) - - clone._memo = {} - - # Apply passed-in variables before the tools - # so the tools can use the new variables - kw = copy_non_reserved_keywords(kw) - new = {} - for key, value in kw.items(): - new[key] = SCons.Subst.scons_subst_once(value, self, key) - clone.Replace(**new) - - apply_tools(clone, tools, toolpath) - - # apply them again in case the tools overwrote them - clone.Replace(**new) - - # Finally, apply any flags to be merged in - if parse_flags: clone.MergeFlags(parse_flags) - - if __debug__: logInstanceCreation(self, 'Environment.EnvironmentClone') - return clone - - def Copy(self, *args, **kw): - global _warn_copy_deprecated - if _warn_copy_deprecated: - msg = "The env.Copy() method is deprecated; use the env.Clone() method instead." - SCons.Warnings.warn(SCons.Warnings.DeprecatedCopyWarning, msg) - _warn_copy_deprecated = False - return self.Clone(*args, **kw) - - def _changed_build(self, dependency, target, prev_ni): - if dependency.changed_state(target, prev_ni): - return 1 - return self.decide_source(dependency, target, prev_ni) - - def _changed_content(self, dependency, target, prev_ni): - return dependency.changed_content(target, prev_ni) - - def _changed_source(self, dependency, target, prev_ni): - target_env = dependency.get_build_env() - type = target_env.get_tgt_sig_type() - if type == 'source': - return target_env.decide_source(dependency, target, prev_ni) - else: - return target_env.decide_target(dependency, target, prev_ni) - - def _changed_timestamp_then_content(self, dependency, target, prev_ni): - return dependency.changed_timestamp_then_content(target, prev_ni) - - def _changed_timestamp_newer(self, dependency, target, prev_ni): - return dependency.changed_timestamp_newer(target, prev_ni) - - def _changed_timestamp_match(self, dependency, target, prev_ni): - return dependency.changed_timestamp_match(target, prev_ni) - - def _copy_from_cache(self, src, dst): - return self.fs.copy(src, dst) - - def _copy2_from_cache(self, src, dst): - return self.fs.copy2(src, dst) - - def Decider(self, function): - copy_function = self._copy2_from_cache - if function in ('MD5', 'content'): - if not SCons.Util.md5: - raise UserError("MD5 signatures are not available in this version of Python.") - function = self._changed_content - elif function == 'MD5-timestamp': - function = self._changed_timestamp_then_content - elif function in ('timestamp-newer', 'make'): - function = self._changed_timestamp_newer - copy_function = self._copy_from_cache - elif function == 'timestamp-match': - function = self._changed_timestamp_match - elif not callable(function): - raise UserError("Unknown Decider value %s" % repr(function)) - - # We don't use AddMethod because we don't want to turn the - # function, which only expects three arguments, into a bound - # method, which would add self as an initial, fourth argument. - self.decide_target = function - self.decide_source = function - - self.copy_from_cache = copy_function - - def Detect(self, progs): - """Return the first available program in progs. - """ - if not SCons.Util.is_List(progs): - progs = [ progs ] - for prog in progs: - path = self.WhereIs(prog) - if path: return prog - return None - - def Dictionary(self, *args): - if not args: - return self._dict - dlist = [self._dict[x] for x in args] - if len(dlist) == 1: - dlist = dlist[0] - return dlist - - def Dump(self, key = None): - """ - Using the standard Python pretty printer, dump the contents of the - scons build environment to stdout. - - If the key passed in is anything other than None, then that will - be used as an index into the build environment dictionary and - whatever is found there will be fed into the pretty printer. Note - that this key is case sensitive. - """ - import pprint - pp = pprint.PrettyPrinter(indent=2) - if key: - dict = self.Dictionary(key) - else: - dict = self.Dictionary() - return pp.pformat(dict) - - def FindIxes(self, paths, prefix, suffix): - """ - Search a list of paths for something that matches the prefix and suffix. - - paths - the list of paths or nodes. - prefix - construction variable for the prefix. - suffix - construction variable for the suffix. - """ - - suffix = self.subst('$'+suffix) - prefix = self.subst('$'+prefix) - - for path in paths: - dir,name = os.path.split(str(path)) - if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix: - return path - - def ParseConfig(self, command, function=None, unique=1): - """ - Use the specified function to parse the output of the command - in order to modify the current environment. The 'command' can - be a string or a list of strings representing a command and - its arguments. 'Function' is an optional argument that takes - the environment, the output of the command, and the unique flag. - If no function is specified, MergeFlags, which treats the output - as the result of a typical 'X-config' command (i.e. gtk-config), - will merge the output into the appropriate variables. - """ - if function is None: - def parse_conf(env, cmd, unique=unique): - return env.MergeFlags(cmd, unique) - function = parse_conf - if SCons.Util.is_List(command): - command = ' '.join(command) - command = self.subst(command) - return function(self, self.backtick(command)) - - def ParseDepends(self, filename, must_exist=None, only_one=0): - """ - Parse a mkdep-style file for explicit dependencies. This is - completely abusable, and should be unnecessary in the "normal" - case of proper SCons configuration, but it may help make - the transition from a Make hierarchy easier for some people - to swallow. It can also be genuinely useful when using a tool - that can write a .d file, but for which writing a scanner would - be too complicated. - """ - filename = self.subst(filename) - try: - fp = open(filename, 'r') - except IOError: - if must_exist: - raise - return - lines = SCons.Util.LogicalLines(fp).readlines() - lines = [l for l in lines if l[0] != '#'] - tdlist = [] - for line in lines: - try: - target, depends = line.split(':', 1) - except (AttributeError, ValueError): - # Throws AttributeError if line isn't a string. Can throw - # ValueError if line doesn't split into two or more elements. - pass - else: - tdlist.append((target.split(), depends.split())) - if only_one: - targets = [] - for td in tdlist: - targets.extend(td[0]) - if len(targets) > 1: - raise SCons.Errors.UserError( - "More than one dependency target found in `%s': %s" - % (filename, targets)) - for target, depends in tdlist: - self.Depends(target, depends) - - def Platform(self, platform): - platform = self.subst(platform) - return SCons.Platform.Platform(platform)(self) - - def Prepend(self, **kw): - """Prepend values to existing construction variables - in an Environment. - """ - kw = copy_non_reserved_keywords(kw) - for key, val in kw.items(): - # It would be easier on the eyes to write this using - # "continue" statements whenever we finish processing an item, - # but Python 1.5.2 apparently doesn't let you use "continue" - # within try:-except: blocks, so we have to nest our code. - try: - orig = self._dict[key] - except KeyError: - # No existing variable in the environment, so just set - # it to the new value. - self._dict[key] = val - else: - try: - # Check if the original looks like a dictionary. - # If it is, we can't just try adding the value because - # dictionaries don't have __add__() methods, and - # things like UserList will incorrectly coerce the - # original dict to a list (which we don't want). - update_dict = orig.update - except AttributeError: - try: - # Most straightforward: just try to add them - # together. This will work in most cases, when the - # original and new values are of compatible types. - self._dict[key] = val + orig - except (KeyError, TypeError): - try: - # Check if the added value is a list. - add_to_val = val.append - except AttributeError: - # The added value isn't a list, but the - # original is (by process of elimination), - # so insert the the new value in the original - # (if there's one to insert). - if val: - orig.insert(0, val) - else: - # The added value is a list, so append - # the original to it (if there's a value - # to append). - if orig: - add_to_val(orig) - self._dict[key] = val - else: - # The original looks like a dictionary, so update it - # based on what we think the value looks like. - if SCons.Util.is_List(val): - for v in val: - orig[v] = None - else: - try: - update_dict(val) - except (AttributeError, TypeError, ValueError): - if SCons.Util.is_Dict(val): - for k, v in val.items(): - orig[k] = v - else: - orig[val] = None - self.scanner_map_delete(kw) - - def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep, - delete_existing=1): - """Prepend path elements to the path 'name' in the 'ENV' - dictionary for this environment. Will only add any particular - path once, and will normpath and normcase all paths to help - assure this. This can also handle the case where the env - variable is a list instead of a string. - - If delete_existing is 0, a newpath which is already in the path - will not be moved to the front (it will be left where it is). - """ - - orig = '' - if envname in self._dict and name in self._dict[envname]: - orig = self._dict[envname][name] - - nv = SCons.Util.PrependPath(orig, newpath, sep, delete_existing, - canonicalize=self._canonicalize) - - if envname not in self._dict: - self._dict[envname] = {} - - self._dict[envname][name] = nv - - def PrependUnique(self, delete_existing=0, **kw): - """Prepend values to existing construction variables - in an Environment, if they're not already there. - If delete_existing is 1, removes existing values first, so - values move to front. - """ - kw = copy_non_reserved_keywords(kw) - for key, val in kw.items(): - if SCons.Util.is_List(val): - val = _delete_duplicates(val, not delete_existing) - if key not in self._dict or self._dict[key] in ('', None): - self._dict[key] = val - elif SCons.Util.is_Dict(self._dict[key]) and \ - SCons.Util.is_Dict(val): - self._dict[key].update(val) - elif SCons.Util.is_List(val): - dk = self._dict[key] - if not SCons.Util.is_List(dk): - dk = [dk] - if delete_existing: - dk = [x for x in dk if x not in val] - else: - val = [x for x in val if x not in dk] - self._dict[key] = val + dk - else: - dk = self._dict[key] - if SCons.Util.is_List(dk): - # By elimination, val is not a list. Since dk is a - # list, wrap val in a list first. - if delete_existing: - dk = [x for x in dk if x not in val] - self._dict[key] = [val] + dk - else: - if not val in dk: - self._dict[key] = [val] + dk - else: - if delete_existing: - dk = [x for x in dk if x not in val] - self._dict[key] = val + dk - self.scanner_map_delete(kw) - - def Replace(self, **kw): - """Replace existing construction variables in an Environment - with new construction variables and/or values. - """ - try: - kwbd = kw['BUILDERS'] - except KeyError: - pass - else: - kwbd = BuilderDict(kwbd,self) - del kw['BUILDERS'] - self.__setitem__('BUILDERS', kwbd) - kw = copy_non_reserved_keywords(kw) - self._update(semi_deepcopy(kw)) - self.scanner_map_delete(kw) - - def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix): - """ - Replace old_prefix with new_prefix and old_suffix with new_suffix. - - env - Environment used to interpolate variables. - path - the path that will be modified. - old_prefix - construction variable for the old prefix. - old_suffix - construction variable for the old suffix. - new_prefix - construction variable for the new prefix. - new_suffix - construction variable for the new suffix. - """ - old_prefix = self.subst('$'+old_prefix) - old_suffix = self.subst('$'+old_suffix) - - new_prefix = self.subst('$'+new_prefix) - new_suffix = self.subst('$'+new_suffix) - - dir,name = os.path.split(str(path)) - if name[:len(old_prefix)] == old_prefix: - name = name[len(old_prefix):] - if name[-len(old_suffix):] == old_suffix: - name = name[:-len(old_suffix)] - return os.path.join(dir, new_prefix+name+new_suffix) - - def SetDefault(self, **kw): - for k in kw.keys(): - if k in self._dict: - del kw[k] - self.Replace(**kw) - - def _find_toolpath_dir(self, tp): - return self.fs.Dir(self.subst(tp)).srcnode().abspath - - def Tool(self, tool, toolpath=None, **kw): - if SCons.Util.is_String(tool): - tool = self.subst(tool) - if toolpath is None: - toolpath = self.get('toolpath', []) - toolpath = list(map(self._find_toolpath_dir, toolpath)) - tool = SCons.Tool.Tool(tool, toolpath, **kw) - tool(self) - - def WhereIs(self, prog, path=None, pathext=None, reject=[]): - """Find prog in the path. - """ - if path is None: - try: - path = self['ENV']['PATH'] - except KeyError: - pass - elif SCons.Util.is_String(path): - path = self.subst(path) - if pathext is None: - try: - pathext = self['ENV']['PATHEXT'] - except KeyError: - pass - elif SCons.Util.is_String(pathext): - pathext = self.subst(pathext) - prog = self.subst(prog) - path = SCons.Util.WhereIs(prog, path, pathext, reject) - if path: return path - return None - - ####################################################################### - # Public methods for doing real "SCons stuff" (manipulating - # dependencies, setting attributes on targets, etc.). These begin - # with upper-case letters. The essential characteristic of methods - # in this section is that they all *should* have corresponding - # same-named global functions. - ####################################################################### - - def Action(self, *args, **kw): - def subst_string(a, self=self): - if SCons.Util.is_String(a): - a = self.subst(a) - return a - nargs = list(map(subst_string, args)) - nkw = self.subst_kw(kw) - return SCons.Action.Action(*nargs, **nkw) - - def AddPreAction(self, files, action): - nodes = self.arg2nodes(files, self.fs.Entry) - action = SCons.Action.Action(action) - uniq = {} - for executor in [n.get_executor() for n in nodes]: - uniq[executor] = 1 - for executor in uniq.keys(): - executor.add_pre_action(action) - return nodes - - def AddPostAction(self, files, action): - nodes = self.arg2nodes(files, self.fs.Entry) - action = SCons.Action.Action(action) - uniq = {} - for executor in [n.get_executor() for n in nodes]: - uniq[executor] = 1 - for executor in uniq.keys(): - executor.add_post_action(action) - return nodes - - def Alias(self, target, source=[], action=None, **kw): - tlist = self.arg2nodes(target, self.ans.Alias) - if not SCons.Util.is_List(source): - source = [source] - source = [_f for _f in source if _f] - - if not action: - if not source: - # There are no source files and no action, so just - # return a target list of classic Alias Nodes, without - # any builder. The externally visible effect is that - # this will make the wrapping Script.BuildTask class - # say that there's "Nothing to be done" for this Alias, - # instead of that it's "up to date." - return tlist - - # No action, but there are sources. Re-call all the target - # builders to add the sources to each target. - result = [] - for t in tlist: - bld = t.get_builder(AliasBuilder) - result.extend(bld(self, t, source)) - return result - - nkw = self.subst_kw(kw) - nkw.update({ - 'action' : SCons.Action.Action(action), - 'source_factory' : self.fs.Entry, - 'multi' : 1, - 'is_explicit' : None, - }) - bld = SCons.Builder.Builder(**nkw) - - # Apply the Builder separately to each target so that the Aliases - # stay separate. If we did one "normal" Builder call with the - # whole target list, then all of the target Aliases would be - # associated under a single Executor. - result = [] - for t in tlist: - # Calling the convert() method will cause a new Executor to be - # created from scratch, so we have to explicitly initialize - # it with the target's existing sources, plus our new ones, - # so nothing gets lost. - b = t.get_builder() - if b is None or b is AliasBuilder: - b = bld - else: - nkw['action'] = b.action + action - b = SCons.Builder.Builder(**nkw) - t.convert() - result.extend(b(self, t, t.sources + source)) - return result - - def AlwaysBuild(self, *targets): - tlist = [] - for t in targets: - tlist.extend(self.arg2nodes(t, self.fs.Entry)) - for t in tlist: - t.set_always_build() - return tlist - - def BuildDir(self, *args, **kw): - msg = """BuildDir() and the build_dir keyword have been deprecated;\n\tuse VariantDir() and the variant_dir keyword instead.""" - SCons.Warnings.warn(SCons.Warnings.DeprecatedBuildDirWarning, msg) - if 'build_dir' in kw: - kw['variant_dir'] = kw['build_dir'] - del kw['build_dir'] - return self.VariantDir(*args, **kw) - - def Builder(self, **kw): - nkw = self.subst_kw(kw) - return SCons.Builder.Builder(**nkw) - - def CacheDir(self, path): - import SCons.CacheDir - if path is not None: - path = self.subst(path) - self._CacheDir_path = path - - def Clean(self, targets, files): - global CleanTargets - tlist = self.arg2nodes(targets, self.fs.Entry) - flist = self.arg2nodes(files, self.fs.Entry) - for t in tlist: - try: - CleanTargets[t].extend(flist) - except KeyError: - CleanTargets[t] = flist - - def Configure(self, *args, **kw): - nargs = [self] - if args: - nargs = nargs + self.subst_list(args)[0] - nkw = self.subst_kw(kw) - nkw['_depth'] = kw.get('_depth', 0) + 1 - try: - nkw['custom_tests'] = self.subst_kw(nkw['custom_tests']) - except KeyError: - pass - return SCons.SConf.SConf(*nargs, **nkw) - - def Command(self, target, source, action, **kw): - """Builds the supplied target files from the supplied - source files using the supplied action. Action may - be any type that the Builder constructor will accept - for an action.""" - bkw = { - 'action' : action, - 'target_factory' : self.fs.Entry, - 'source_factory' : self.fs.Entry, - } - try: bkw['source_scanner'] = kw['source_scanner'] - except KeyError: pass - else: del kw['source_scanner'] - bld = SCons.Builder.Builder(**bkw) - return bld(self, target, source, **kw) - - def Depends(self, target, dependency): - """Explicity specify that 'target's depend on 'dependency'.""" - tlist = self.arg2nodes(target, self.fs.Entry) - dlist = self.arg2nodes(dependency, self.fs.Entry) - for t in tlist: - t.add_dependency(dlist) - return tlist - - def Dir(self, name, *args, **kw): - """ - """ - s = self.subst(name) - if SCons.Util.is_Sequence(s): - result=[] - for e in s: - result.append(self.fs.Dir(e, *args, **kw)) - return result - return self.fs.Dir(s, *args, **kw) - - def NoClean(self, *targets): - """Tags a target so that it will not be cleaned by -c""" - tlist = [] - for t in targets: - tlist.extend(self.arg2nodes(t, self.fs.Entry)) - for t in tlist: - t.set_noclean() - return tlist - - def NoCache(self, *targets): - """Tags a target so that it will not be cached""" - tlist = [] - for t in targets: - tlist.extend(self.arg2nodes(t, self.fs.Entry)) - for t in tlist: - t.set_nocache() - return tlist - - def Entry(self, name, *args, **kw): - """ - """ - s = self.subst(name) - if SCons.Util.is_Sequence(s): - result=[] - for e in s: - result.append(self.fs.Entry(e, *args, **kw)) - return result - return self.fs.Entry(s, *args, **kw) - - def Environment(self, **kw): - return SCons.Environment.Environment(**self.subst_kw(kw)) - - def Execute(self, action, *args, **kw): - """Directly execute an action through an Environment - """ - action = self.Action(action, *args, **kw) - result = action([], [], self) - if isinstance(result, SCons.Errors.BuildError): - errstr = result.errstr - if result.filename: - errstr = result.filename + ': ' + errstr - sys.stderr.write("scons: *** %s\n" % errstr) - return result.status - else: - return result - - def File(self, name, *args, **kw): - """ - """ - s = self.subst(name) - if SCons.Util.is_Sequence(s): - result=[] - for e in s: - result.append(self.fs.File(e, *args, **kw)) - return result - return self.fs.File(s, *args, **kw) - - def FindFile(self, file, dirs): - file = self.subst(file) - nodes = self.arg2nodes(dirs, self.fs.Dir) - return SCons.Node.FS.find_file(file, tuple(nodes)) - - def Flatten(self, sequence): - return SCons.Util.flatten(sequence) - - def GetBuildPath(self, files): - result = list(map(str, self.arg2nodes(files, self.fs.Entry))) - if SCons.Util.is_List(files): - return result - else: - return result[0] - - def Glob(self, pattern, ondisk=True, source=False, strings=False): - return self.fs.Glob(self.subst(pattern), ondisk, source, strings) - - def Ignore(self, target, dependency): - """Ignore a dependency.""" - tlist = self.arg2nodes(target, self.fs.Entry) - dlist = self.arg2nodes(dependency, self.fs.Entry) - for t in tlist: - t.add_ignore(dlist) - return tlist - - def Literal(self, string): - return SCons.Subst.Literal(string) - - def Local(self, *targets): - ret = [] - for targ in targets: - if isinstance(targ, SCons.Node.Node): - targ.set_local() - ret.append(targ) - else: - for t in self.arg2nodes(targ, self.fs.Entry): - t.set_local() - ret.append(t) - return ret - - def Precious(self, *targets): - tlist = [] - for t in targets: - tlist.extend(self.arg2nodes(t, self.fs.Entry)) - for t in tlist: - t.set_precious() - return tlist - - def Repository(self, *dirs, **kw): - dirs = self.arg2nodes(list(dirs), self.fs.Dir) - self.fs.Repository(*dirs, **kw) - - def Requires(self, target, prerequisite): - """Specify that 'prerequisite' must be built before 'target', - (but 'target' does not actually depend on 'prerequisite' - and need not be rebuilt if it changes).""" - tlist = self.arg2nodes(target, self.fs.Entry) - plist = self.arg2nodes(prerequisite, self.fs.Entry) - for t in tlist: - t.add_prerequisite(plist) - return tlist - - def Scanner(self, *args, **kw): - nargs = [] - for arg in args: - if SCons.Util.is_String(arg): - arg = self.subst(arg) - nargs.append(arg) - nkw = self.subst_kw(kw) - return SCons.Scanner.Base(*nargs, **nkw) - - def SConsignFile(self, name=".sconsign", dbm_module=None): - if name is not None: - name = self.subst(name) - if not os.path.isabs(name): - name = os.path.join(str(self.fs.SConstruct_dir), name) - if name: - name = os.path.normpath(name) - sconsign_dir = os.path.dirname(name) - if sconsign_dir and not os.path.exists(sconsign_dir): - self.Execute(SCons.Defaults.Mkdir(sconsign_dir)) - SCons.SConsign.File(name, dbm_module) - - def SideEffect(self, side_effect, target): - """Tell scons that side_effects are built as side - effects of building targets.""" - side_effects = self.arg2nodes(side_effect, self.fs.Entry) - targets = self.arg2nodes(target, self.fs.Entry) - - for side_effect in side_effects: - if side_effect.multiple_side_effect_has_builder(): - raise SCons.Errors.UserError("Multiple ways to build the same target were specified for: %s" % str(side_effect)) - side_effect.add_source(targets) - side_effect.side_effect = 1 - self.Precious(side_effect) - for target in targets: - target.side_effects.append(side_effect) - return side_effects - - def SourceCode(self, entry, builder): - """Arrange for a source code builder for (part of) a tree.""" - msg = """SourceCode() has been deprecated and there is no replacement. -\tIf you need this function, please contact dev@scons.tigris.org.""" - SCons.Warnings.warn(SCons.Warnings.DeprecatedSourceCodeWarning, msg) - entries = self.arg2nodes(entry, self.fs.Entry) - for entry in entries: - entry.set_src_builder(builder) - return entries - - def SourceSignatures(self, type): - global _warn_source_signatures_deprecated - if _warn_source_signatures_deprecated: - msg = "The env.SourceSignatures() method is deprecated;\n" + \ - "\tconvert your build to use the env.Decider() method instead." - SCons.Warnings.warn(SCons.Warnings.DeprecatedSourceSignaturesWarning, msg) - _warn_source_signatures_deprecated = False - type = self.subst(type) - self.src_sig_type = type - if type == 'MD5': - if not SCons.Util.md5: - raise UserError("MD5 signatures are not available in this version of Python.") - self.decide_source = self._changed_content - elif type == 'timestamp': - self.decide_source = self._changed_timestamp_match - else: - raise UserError("Unknown source signature type '%s'" % type) - - def Split(self, arg): - """This function converts a string or list into a list of strings - or Nodes. This makes things easier for users by allowing files to - be specified as a white-space separated list to be split. - The input rules are: - - A single string containing names separated by spaces. These will be - split apart at the spaces. - - A single Node instance - - A list containing either strings or Node instances. Any strings - in the list are not split at spaces. - In all cases, the function returns a list of Nodes and strings.""" - if SCons.Util.is_List(arg): - return list(map(self.subst, arg)) - elif SCons.Util.is_String(arg): - return self.subst(arg).split() - else: - return [self.subst(arg)] - - def TargetSignatures(self, type): - global _warn_target_signatures_deprecated - if _warn_target_signatures_deprecated: - msg = "The env.TargetSignatures() method is deprecated;\n" + \ - "\tconvert your build to use the env.Decider() method instead." - SCons.Warnings.warn(SCons.Warnings.DeprecatedTargetSignaturesWarning, msg) - _warn_target_signatures_deprecated = False - type = self.subst(type) - self.tgt_sig_type = type - if type in ('MD5', 'content'): - if not SCons.Util.md5: - raise UserError("MD5 signatures are not available in this version of Python.") - self.decide_target = self._changed_content - elif type == 'timestamp': - self.decide_target = self._changed_timestamp_match - elif type == 'build': - self.decide_target = self._changed_build - elif type == 'source': - self.decide_target = self._changed_source - else: - raise UserError("Unknown target signature type '%s'"%type) - - def Value(self, value, built_value=None): - """ - """ - return SCons.Node.Python.Value(value, built_value) - - def VariantDir(self, variant_dir, src_dir, duplicate=1): - variant_dir = self.arg2nodes(variant_dir, self.fs.Dir)[0] - src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0] - self.fs.VariantDir(variant_dir, src_dir, duplicate) - - def FindSourceFiles(self, node='.'): - """ returns a list of all source files. - """ - node = self.arg2nodes(node, self.fs.Entry)[0] - - sources = [] - def build_source(ss): - for s in ss: - if isinstance(s, SCons.Node.FS.Dir): - build_source(s.all_children()) - elif s.has_builder(): - build_source(s.sources) - elif isinstance(s.disambiguate(), SCons.Node.FS.File): - sources.append(s) - build_source(node.all_children()) - - def final_source(node): - while (node != node.srcnode()): - node = node.srcnode() - return node - sources = map( final_source, sources ); - # remove duplicates - return list(set(sources)) - - def FindInstalledFiles(self): - """ returns the list of all targets of the Install and InstallAs Builder. - """ - from SCons.Tool import install - if install._UNIQUE_INSTALLED_FILES is None: - install._UNIQUE_INSTALLED_FILES = SCons.Util.uniquer_hashables(install._INSTALLED_FILES) - return install._UNIQUE_INSTALLED_FILES - -class OverrideEnvironment(Base): - """A proxy that overrides variables in a wrapped construction - environment by returning values from an overrides dictionary in - preference to values from the underlying subject environment. - - This is a lightweight (I hope) proxy that passes through most use of - attributes to the underlying Environment.Base class, but has just - enough additional methods defined to act like a real construction - environment with overridden values. It can wrap either a Base - construction environment, or another OverrideEnvironment, which - can in turn nest arbitrary OverrideEnvironments... - - Note that we do *not* call the underlying base class - (SubsitutionEnvironment) initialization, because we get most of those - from proxying the attributes of the subject construction environment. - But because we subclass SubstitutionEnvironment, this class also - has inherited arg2nodes() and subst*() methods; those methods can't - be proxied because they need *this* object's methods to fetch the - values from the overrides dictionary. - """ - - def __init__(self, subject, overrides={}): - if __debug__: logInstanceCreation(self, 'Environment.OverrideEnvironment') - self.__dict__['__subject'] = subject - self.__dict__['overrides'] = overrides - - # Methods that make this class act like a proxy. - def __getattr__(self, name): - return getattr(self.__dict__['__subject'], name) - def __setattr__(self, name, value): - setattr(self.__dict__['__subject'], name, value) - - # Methods that make this class act like a dictionary. - def __getitem__(self, key): - try: - return self.__dict__['overrides'][key] - except KeyError: - return self.__dict__['__subject'].__getitem__(key) - def __setitem__(self, key, value): - if not is_valid_construction_var(key): - raise SCons.Errors.UserError("Illegal construction variable `%s'" % key) - self.__dict__['overrides'][key] = value - def __delitem__(self, key): - try: - del self.__dict__['overrides'][key] - except KeyError: - deleted = 0 - else: - deleted = 1 - try: - result = self.__dict__['__subject'].__delitem__(key) - except KeyError: - if not deleted: - raise - result = None - return result - def get(self, key, default=None): - """Emulates the get() method of dictionaries.""" - try: - return self.__dict__['overrides'][key] - except KeyError: - return self.__dict__['__subject'].get(key, default) - def has_key(self, key): - try: - self.__dict__['overrides'][key] - return 1 - except KeyError: - return key in self.__dict__['__subject'] - def __contains__(self, key): - if self.__dict__['overrides'].__contains__(key): - return 1 - return self.__dict__['__subject'].__contains__(key) - def Dictionary(self): - """Emulates the items() method of dictionaries.""" - d = self.__dict__['__subject'].Dictionary().copy() - d.update(self.__dict__['overrides']) - return d - def items(self): - """Emulates the items() method of dictionaries.""" - return list(self.Dictionary().items()) - - # Overridden private construction environment methods. - def _update(self, dict): - """Update an environment's values directly, bypassing the normal - checks that occur when users try to set items. - """ - self.__dict__['overrides'].update(dict) - - def gvars(self): - return self.__dict__['__subject'].gvars() - - def lvars(self): - lvars = self.__dict__['__subject'].lvars() - lvars.update(self.__dict__['overrides']) - return lvars - - # Overridden public construction environment methods. - def Replace(self, **kw): - kw = copy_non_reserved_keywords(kw) - self.__dict__['overrides'].update(semi_deepcopy(kw)) - -# The entry point that will be used by the external world -# to refer to a construction environment. This allows the wrapper -# interface to extend a construction environment for its own purposes -# by subclassing SCons.Environment.Base and then assigning the -# class to SCons.Environment.Environment. - -Environment = Base - -# An entry point for returning a proxy subclass instance that overrides -# the subst*() methods so they don't actually perform construction -# variable substitution. This is specifically intended to be the shim -# layer in between global function calls (which don't want construction -# variable substitution) and the DefaultEnvironment() (which would -# substitute variables if left to its own devices).""" -# -# We have to wrap this in a function that allows us to delay definition of -# the class until it's necessary, so that when it subclasses Environment -# it will pick up whatever Environment subclass the wrapper interface -# might have assigned to SCons.Environment.Environment. - -def NoSubstitutionProxy(subject): - class _NoSubstitutionProxy(Environment): - def __init__(self, subject): - self.__dict__['__subject'] = subject - def __getattr__(self, name): - return getattr(self.__dict__['__subject'], name) - def __setattr__(self, name, value): - return setattr(self.__dict__['__subject'], name, value) - def executor_to_lvars(self, kwdict): - if kwdict.has_key('executor'): - kwdict['lvars'] = kwdict['executor'].get_lvars() - del kwdict['executor'] - else: - kwdict['lvars'] = {} - def raw_to_mode(self, dict): - try: - raw = dict['raw'] - except KeyError: - pass - else: - del dict['raw'] - dict['mode'] = raw - def subst(self, string, *args, **kwargs): - return string - def subst_kw(self, kw, *args, **kwargs): - return kw - def subst_list(self, string, *args, **kwargs): - nargs = (string, self,) + args - nkw = kwargs.copy() - nkw['gvars'] = {} - self.executor_to_lvars(nkw) - self.raw_to_mode(nkw) - return SCons.Subst.scons_subst_list(*nargs, **nkw) - def subst_target_source(self, string, *args, **kwargs): - nargs = (string, self,) + args - nkw = kwargs.copy() - nkw['gvars'] = {} - self.executor_to_lvars(nkw) - self.raw_to_mode(nkw) - return SCons.Subst.scons_subst(*nargs, **nkw) - return _NoSubstitutionProxy(subject) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Errors.py b/scons/scons-local-2.2.0/SCons/Errors.py deleted file mode 100644 index 8541c68ab..000000000 --- a/scons/scons-local-2.2.0/SCons/Errors.py +++ /dev/null @@ -1,205 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -"""SCons.Errors - -This file contains the exception classes used to handle internal -and user errors in SCons. - -""" - -__revision__ = "src/engine/SCons/Errors.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Util - -import exceptions - -class BuildError(Exception): - """ Errors occuring while building. - - BuildError have the following attributes: - - Information about the cause of the build error: - ----------------------------------------------- - - errstr : a description of the error message - - status : the return code of the action that caused the build - error. Must be set to a non-zero value even if the - build error is not due to an action returning a - non-zero returned code. - - exitstatus : SCons exit status due to this build error. - Must be nonzero unless due to an explicit Exit() - call. Not always the same as status, since - actions return a status code that should be - respected, but SCons typically exits with 2 - irrespective of the return value of the failed - action. - - filename : The name of the file or directory that caused the - build error. Set to None if no files are associated with - this error. This might be different from the target - being built. For example, failure to create the - directory in which the target file will appear. It - can be None if the error is not due to a particular - filename. - - exc_info : Info about exception that caused the build - error. Set to (None, None, None) if this build - error is not due to an exception. - - - Information about the cause of the location of the error: - --------------------------------------------------------- - - node : the error occured while building this target node(s) - - executor : the executor that caused the build to fail (might - be None if the build failures is not due to the - executor failing) - - action : the action that caused the build to fail (might be - None if the build failures is not due to the an - action failure) - - command : the command line for the action that caused the - build to fail (might be None if the build failures - is not due to the an action failure) - """ - - def __init__(self, - node=None, errstr="Unknown error", status=2, exitstatus=2, - filename=None, executor=None, action=None, command=None, - exc_info=(None, None, None)): - - self.errstr = errstr - self.status = status - self.exitstatus = exitstatus - self.filename = filename - self.exc_info = exc_info - - self.node = node - self.executor = executor - self.action = action - self.command = command - - Exception.__init__(self, node, errstr, status, exitstatus, filename, - executor, action, command, exc_info) - - def __str__(self): - if self.filename: - return self.filename + ': ' + self.errstr - else: - return self.errstr - -class InternalError(Exception): - pass - -class UserError(Exception): - pass - -class StopError(Exception): - pass - -class EnvironmentError(Exception): - pass - -class MSVCError(IOError): - pass - -class ExplicitExit(Exception): - def __init__(self, node=None, status=None, *args): - self.node = node - self.status = status - self.exitstatus = status - Exception.__init__(self, *args) - -def convert_to_BuildError(status, exc_info=None): - """ - Convert any return code a BuildError Exception. - - `status' can either be a return code or an Exception. - The buildError.status we set here will normally be - used as the exit status of the "scons" process. - """ - if not exc_info and isinstance(status, Exception): - exc_info = (status.__class__, status, None) - - if isinstance(status, BuildError): - buildError = status - buildError.exitstatus = 2 # always exit with 2 on build errors - elif isinstance(status, ExplicitExit): - status = status.status - errstr = 'Explicit exit, status %s' % status - buildError = BuildError( - errstr=errstr, - status=status, # might be 0, OK here - exitstatus=status, # might be 0, OK here - exc_info=exc_info) - elif isinstance(status, (StopError, UserError)): - buildError = BuildError( - errstr=str(status), - status=2, - exitstatus=2, - exc_info=exc_info) - elif isinstance(status, exceptions.EnvironmentError): - # If an IOError/OSError happens, raise a BuildError. - # Report the name of the file or directory that caused the - # error, which might be different from the target being built - # (for example, failure to create the directory in which the - # target file will appear). - try: filename = status.filename - except AttributeError: filename = None - buildError = BuildError( - errstr=status.strerror, - status=status.errno, - exitstatus=2, - filename=filename, - exc_info=exc_info) - elif isinstance(status, Exception): - buildError = BuildError( - errstr='%s : %s' % (status.__class__.__name__, status), - status=2, - exitstatus=2, - exc_info=exc_info) - elif SCons.Util.is_String(status): - buildError = BuildError( - errstr=status, - status=2, - exitstatus=2) - else: - buildError = BuildError( - errstr="Error %s" % status, - status=status, - exitstatus=2) - - #import sys - #sys.stderr.write("convert_to_BuildError: status %s => (errstr %s, status %s)"%(status,buildError.errstr, buildError.status)) - return buildError - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Executor.py b/scons/scons-local-2.2.0/SCons/Executor.py deleted file mode 100644 index 9ea6e631d..000000000 --- a/scons/scons-local-2.2.0/SCons/Executor.py +++ /dev/null @@ -1,633 +0,0 @@ -"""SCons.Executor - -A module for executing actions with specific lists of target and source -Nodes. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Executor.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import collections - -from SCons.Debug import logInstanceCreation -import SCons.Errors -import SCons.Memoize - - -class Batch(object): - """Remembers exact association between targets - and sources of executor.""" - def __init__(self, targets=[], sources=[]): - self.targets = targets - self.sources = sources - - - -class TSList(collections.UserList): - """A class that implements $TARGETS or $SOURCES expansions by wrapping - an executor Method. This class is used in the Executor.lvars() - to delay creation of NodeList objects until they're needed. - - Note that we subclass collections.UserList purely so that the - is_Sequence() function will identify an object of this class as - a list during variable expansion. We're not really using any - collections.UserList methods in practice. - """ - def __init__(self, func): - self.func = func - def __getattr__(self, attr): - nl = self.func() - return getattr(nl, attr) - def __getitem__(self, i): - nl = self.func() - return nl[i] - def __getslice__(self, i, j): - nl = self.func() - i = max(i, 0); j = max(j, 0) - return nl[i:j] - def __str__(self): - nl = self.func() - return str(nl) - def __repr__(self): - nl = self.func() - return repr(nl) - -class TSObject(object): - """A class that implements $TARGET or $SOURCE expansions by wrapping - an Executor method. - """ - def __init__(self, func): - self.func = func - def __getattr__(self, attr): - n = self.func() - return getattr(n, attr) - def __str__(self): - n = self.func() - if n: - return str(n) - return '' - def __repr__(self): - n = self.func() - if n: - return repr(n) - return '' - -def rfile(node): - """ - A function to return the results of a Node's rfile() method, - if it exists, and the Node itself otherwise (if it's a Value - Node, e.g.). - """ - try: - rfile = node.rfile - except AttributeError: - return node - else: - return rfile() - - -class Executor(object): - """A class for controlling instances of executing an action. - - This largely exists to hold a single association of an action, - environment, list of environment override dictionaries, targets - and sources for later processing as needed. - """ - - if SCons.Memoize.use_memoizer: - __metaclass__ = SCons.Memoize.Memoized_Metaclass - - memoizer_counters = [] - - def __init__(self, action, env=None, overridelist=[{}], - targets=[], sources=[], builder_kw={}): - if __debug__: logInstanceCreation(self, 'Executor.Executor') - self.set_action_list(action) - self.pre_actions = [] - self.post_actions = [] - self.env = env - self.overridelist = overridelist - if targets or sources: - self.batches = [Batch(targets[:], sources[:])] - else: - self.batches = [] - self.builder_kw = builder_kw - self._memo = {} - - def get_lvars(self): - try: - return self.lvars - except AttributeError: - self.lvars = { - 'CHANGED_SOURCES' : TSList(self._get_changed_sources), - 'CHANGED_TARGETS' : TSList(self._get_changed_targets), - 'SOURCE' : TSObject(self._get_source), - 'SOURCES' : TSList(self._get_sources), - 'TARGET' : TSObject(self._get_target), - 'TARGETS' : TSList(self._get_targets), - 'UNCHANGED_SOURCES' : TSList(self._get_unchanged_sources), - 'UNCHANGED_TARGETS' : TSList(self._get_unchanged_targets), - } - return self.lvars - - def _get_changes(self): - cs = [] - ct = [] - us = [] - ut = [] - for b in self.batches: - if b.targets[0].is_up_to_date(): - us.extend(list(map(rfile, b.sources))) - ut.extend(b.targets) - else: - cs.extend(list(map(rfile, b.sources))) - ct.extend(b.targets) - self._changed_sources_list = SCons.Util.NodeList(cs) - self._changed_targets_list = SCons.Util.NodeList(ct) - self._unchanged_sources_list = SCons.Util.NodeList(us) - self._unchanged_targets_list = SCons.Util.NodeList(ut) - - def _get_changed_sources(self, *args, **kw): - try: - return self._changed_sources_list - except AttributeError: - self._get_changes() - return self._changed_sources_list - - def _get_changed_targets(self, *args, **kw): - try: - return self._changed_targets_list - except AttributeError: - self._get_changes() - return self._changed_targets_list - - def _get_source(self, *args, **kw): - #return SCons.Util.NodeList([rfile(self.batches[0].sources[0]).get_subst_proxy()]) - return rfile(self.batches[0].sources[0]).get_subst_proxy() - - def _get_sources(self, *args, **kw): - return SCons.Util.NodeList([rfile(n).get_subst_proxy() for n in self.get_all_sources()]) - - def _get_target(self, *args, **kw): - #return SCons.Util.NodeList([self.batches[0].targets[0].get_subst_proxy()]) - return self.batches[0].targets[0].get_subst_proxy() - - def _get_targets(self, *args, **kw): - return SCons.Util.NodeList([n.get_subst_proxy() for n in self.get_all_targets()]) - - def _get_unchanged_sources(self, *args, **kw): - try: - return self._unchanged_sources_list - except AttributeError: - self._get_changes() - return self._unchanged_sources_list - - def _get_unchanged_targets(self, *args, **kw): - try: - return self._unchanged_targets_list - except AttributeError: - self._get_changes() - return self._unchanged_targets_list - - def get_action_targets(self): - if not self.action_list: - return [] - targets_string = self.action_list[0].get_targets(self.env, self) - if targets_string[0] == '$': - targets_string = targets_string[1:] - return self.get_lvars()[targets_string] - - def set_action_list(self, action): - import SCons.Util - if not SCons.Util.is_List(action): - if not action: - import SCons.Errors - raise SCons.Errors.UserError("Executor must have an action.") - action = [action] - self.action_list = action - - def get_action_list(self): - return self.pre_actions + self.action_list + self.post_actions - - def get_all_targets(self): - """Returns all targets for all batches of this Executor.""" - result = [] - for batch in self.batches: - result.extend(batch.targets) - return result - - def get_all_sources(self): - """Returns all sources for all batches of this Executor.""" - result = [] - for batch in self.batches: - result.extend(batch.sources) - return result - - def get_all_children(self): - """Returns all unique children (dependencies) for all batches - of this Executor. - - The Taskmaster can recognize when it's already evaluated a - Node, so we don't have to make this list unique for its intended - canonical use case, but we expect there to be a lot of redundancy - (long lists of batched .cc files #including the same .h files - over and over), so removing the duplicates once up front should - save the Taskmaster a lot of work. - """ - result = SCons.Util.UniqueList([]) - for target in self.get_all_targets(): - result.extend(target.children()) - return result - - def get_all_prerequisites(self): - """Returns all unique (order-only) prerequisites for all batches - of this Executor. - """ - result = SCons.Util.UniqueList([]) - for target in self.get_all_targets(): - result.extend(target.prerequisites) - return result - - def get_action_side_effects(self): - - """Returns all side effects for all batches of this - Executor used by the underlying Action. - """ - result = SCons.Util.UniqueList([]) - for target in self.get_action_targets(): - result.extend(target.side_effects) - return result - - memoizer_counters.append(SCons.Memoize.CountValue('get_build_env')) - - def get_build_env(self): - """Fetch or create the appropriate build Environment - for this Executor. - """ - try: - return self._memo['get_build_env'] - except KeyError: - pass - - # Create the build environment instance with appropriate - # overrides. These get evaluated against the current - # environment's construction variables so that users can - # add to existing values by referencing the variable in - # the expansion. - overrides = {} - for odict in self.overridelist: - overrides.update(odict) - - import SCons.Defaults - env = self.env or SCons.Defaults.DefaultEnvironment() - build_env = env.Override(overrides) - - self._memo['get_build_env'] = build_env - - return build_env - - def get_build_scanner_path(self, scanner): - """Fetch the scanner path for this executor's targets and sources. - """ - env = self.get_build_env() - try: - cwd = self.batches[0].targets[0].cwd - except (IndexError, AttributeError): - cwd = None - return scanner.path(env, cwd, - self.get_all_targets(), - self.get_all_sources()) - - def get_kw(self, kw={}): - result = self.builder_kw.copy() - result.update(kw) - result['executor'] = self - return result - - def do_nothing(self, target, kw): - return 0 - - def do_execute(self, target, kw): - """Actually execute the action list.""" - env = self.get_build_env() - kw = self.get_kw(kw) - status = 0 - for act in self.get_action_list(): - #args = (self.get_all_targets(), self.get_all_sources(), env) - args = ([], [], env) - status = act(*args, **kw) - if isinstance(status, SCons.Errors.BuildError): - status.executor = self - raise status - elif status: - msg = "Error %s" % status - raise SCons.Errors.BuildError( - errstr=msg, - node=self.batches[0].targets, - executor=self, - action=act) - return status - - # use extra indirection because with new-style objects (Python 2.2 - # and above) we can't override special methods, and nullify() needs - # to be able to do this. - - def __call__(self, target, **kw): - return self.do_execute(target, kw) - - def cleanup(self): - self._memo = {} - - def add_sources(self, sources): - """Add source files to this Executor's list. This is necessary - for "multi" Builders that can be called repeatedly to build up - a source file list for a given target.""" - # TODO(batch): extend to multiple batches - assert (len(self.batches) == 1) - # TODO(batch): remove duplicates? - sources = [x for x in sources if x not in self.batches[0].sources] - self.batches[0].sources.extend(sources) - - def get_sources(self): - return self.batches[0].sources - - def add_batch(self, targets, sources): - """Add pair of associated target and source to this Executor's list. - This is necessary for "batch" Builders that can be called repeatedly - to build up a list of matching target and source files that will be - used in order to update multiple target files at once from multiple - corresponding source files, for tools like MSVC that support it.""" - self.batches.append(Batch(targets, sources)) - - def prepare(self): - """ - Preparatory checks for whether this Executor can go ahead - and (try to) build its targets. - """ - for s in self.get_all_sources(): - if s.missing(): - msg = "Source `%s' not found, needed by target `%s'." - raise SCons.Errors.StopError(msg % (s, self.batches[0].targets[0])) - - def add_pre_action(self, action): - self.pre_actions.append(action) - - def add_post_action(self, action): - self.post_actions.append(action) - - # another extra indirection for new-style objects and nullify... - - def my_str(self): - env = self.get_build_env() - return "\n".join([action.genstring(self.get_all_targets(), - self.get_all_sources(), - env) - for action in self.get_action_list()]) - - - def __str__(self): - return self.my_str() - - def nullify(self): - self.cleanup() - self.do_execute = self.do_nothing - self.my_str = lambda: '' - - memoizer_counters.append(SCons.Memoize.CountValue('get_contents')) - - def get_contents(self): - """Fetch the signature contents. This is the main reason this - class exists, so we can compute this once and cache it regardless - of how many target or source Nodes there are. - """ - try: - return self._memo['get_contents'] - except KeyError: - pass - env = self.get_build_env() - result = "".join([action.get_contents(self.get_all_targets(), - self.get_all_sources(), - env) - for action in self.get_action_list()]) - self._memo['get_contents'] = result - return result - - def get_timestamp(self): - """Fetch a time stamp for this Executor. We don't have one, of - course (only files do), but this is the interface used by the - timestamp module. - """ - return 0 - - def scan_targets(self, scanner): - # TODO(batch): scan by batches - self.scan(scanner, self.get_all_targets()) - - def scan_sources(self, scanner): - # TODO(batch): scan by batches - if self.batches[0].sources: - self.scan(scanner, self.get_all_sources()) - - def scan(self, scanner, node_list): - """Scan a list of this Executor's files (targets or sources) for - implicit dependencies and update all of the targets with them. - This essentially short-circuits an N*M scan of the sources for - each individual target, which is a hell of a lot more efficient. - """ - env = self.get_build_env() - - # TODO(batch): scan by batches) - deps = [] - if scanner: - for node in node_list: - node.disambiguate() - s = scanner.select(node) - if not s: - continue - path = self.get_build_scanner_path(s) - deps.extend(node.get_implicit_deps(env, s, path)) - else: - kw = self.get_kw() - for node in node_list: - node.disambiguate() - scanner = node.get_env_scanner(env, kw) - if not scanner: - continue - scanner = scanner.select(node) - if not scanner: - continue - path = self.get_build_scanner_path(scanner) - deps.extend(node.get_implicit_deps(env, scanner, path)) - - deps.extend(self.get_implicit_deps()) - - for tgt in self.get_all_targets(): - tgt.add_to_implicit(deps) - - def _get_unignored_sources_key(self, node, ignore=()): - return (node,) + tuple(ignore) - - memoizer_counters.append(SCons.Memoize.CountDict('get_unignored_sources', _get_unignored_sources_key)) - - def get_unignored_sources(self, node, ignore=()): - key = (node,) + tuple(ignore) - try: - memo_dict = self._memo['get_unignored_sources'] - except KeyError: - memo_dict = {} - self._memo['get_unignored_sources'] = memo_dict - else: - try: - return memo_dict[key] - except KeyError: - pass - - if node: - # TODO: better way to do this (it's a linear search, - # but it may not be critical path)? - sourcelist = [] - for b in self.batches: - if node in b.targets: - sourcelist = b.sources - break - else: - sourcelist = self.get_all_sources() - if ignore: - idict = {} - for i in ignore: - idict[i] = 1 - sourcelist = [s for s in sourcelist if s not in idict] - - memo_dict[key] = sourcelist - - return sourcelist - - def get_implicit_deps(self): - """Return the executor's implicit dependencies, i.e. the nodes of - the commands to be executed.""" - result = [] - build_env = self.get_build_env() - for act in self.get_action_list(): - deps = act.get_implicit_deps(self.get_all_targets(), - self.get_all_sources(), - build_env) - result.extend(deps) - return result - - - -_batch_executors = {} - -def GetBatchExecutor(key): - return _batch_executors[key] - -def AddBatchExecutor(key, executor): - assert key not in _batch_executors - _batch_executors[key] = executor - -nullenv = None - - -def get_NullEnvironment(): - """Use singleton pattern for Null Environments.""" - global nullenv - - import SCons.Util - class NullEnvironment(SCons.Util.Null): - import SCons.CacheDir - _CacheDir_path = None - _CacheDir = SCons.CacheDir.CacheDir(None) - def get_CacheDir(self): - return self._CacheDir - - if not nullenv: - nullenv = NullEnvironment() - return nullenv - -class Null(object): - """A null Executor, with a null build Environment, that does - nothing when the rest of the methods call it. - - This might be able to disapper when we refactor things to - disassociate Builders from Nodes entirely, so we're not - going to worry about unit tests for this--at least for now. - """ - def __init__(self, *args, **kw): - if __debug__: logInstanceCreation(self, 'Executor.Null') - self.batches = [Batch(kw['targets'][:], [])] - def get_build_env(self): - return get_NullEnvironment() - def get_build_scanner_path(self): - return None - def cleanup(self): - pass - def prepare(self): - pass - def get_unignored_sources(self, *args, **kw): - return tuple(()) - def get_action_targets(self): - return [] - def get_action_list(self): - return [] - def get_all_targets(self): - return self.batches[0].targets - def get_all_sources(self): - return self.batches[0].targets[0].sources - def get_all_children(self): - return self.batches[0].targets[0].children() - def get_all_prerequisites(self): - return [] - def get_action_side_effects(self): - return [] - def __call__(self, *args, **kw): - return 0 - def get_contents(self): - return '' - def _morph(self): - """Morph this Null executor to a real Executor object.""" - batches = self.batches - self.__class__ = Executor - self.__init__([]) - self.batches = batches - - # The following methods require morphing this Null Executor to a - # real Executor object. - - def add_pre_action(self, action): - self._morph() - self.add_pre_action(action) - def add_post_action(self, action): - self._morph() - self.add_post_action(action) - def set_action_list(self, action): - self._morph() - self.set_action_list(action) - - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Job.py b/scons/scons-local-2.2.0/SCons/Job.py deleted file mode 100644 index 342f55e98..000000000 --- a/scons/scons-local-2.2.0/SCons/Job.py +++ /dev/null @@ -1,435 +0,0 @@ -"""SCons.Job - -This module defines the Serial and Parallel classes that execute tasks to -complete a build. The Jobs class provides a higher level interface to start, -stop, and wait on jobs. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Job.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.compat - -import os -import signal - -import SCons.Errors - -# The default stack size (in kilobytes) of the threads used to execute -# jobs in parallel. -# -# We use a stack size of 256 kilobytes. The default on some platforms -# is too large and prevents us from creating enough threads to fully -# parallelized the build. For example, the default stack size on linux -# is 8 MBytes. - -explicit_stack_size = None -default_stack_size = 256 - -interrupt_msg = 'Build interrupted.' - - -class InterruptState(object): - def __init__(self): - self.interrupted = False - - def set(self): - self.interrupted = True - - def __call__(self): - return self.interrupted - - -class Jobs(object): - """An instance of this class initializes N jobs, and provides - methods for starting, stopping, and waiting on all N jobs. - """ - - def __init__(self, num, taskmaster): - """ - create 'num' jobs using the given taskmaster. - - If 'num' is 1 or less, then a serial job will be used, - otherwise a parallel job with 'num' worker threads will - be used. - - The 'num_jobs' attribute will be set to the actual number of jobs - allocated. If more than one job is requested but the Parallel - class can't do it, it gets reset to 1. Wrapping interfaces that - care should check the value of 'num_jobs' after initialization. - """ - - self.job = None - if num > 1: - stack_size = explicit_stack_size - if stack_size is None: - stack_size = default_stack_size - - try: - self.job = Parallel(taskmaster, num, stack_size) - self.num_jobs = num - except NameError: - pass - if self.job is None: - self.job = Serial(taskmaster) - self.num_jobs = 1 - - def run(self, postfunc=lambda: None): - """Run the jobs. - - postfunc() will be invoked after the jobs has run. It will be - invoked even if the jobs are interrupted by a keyboard - interrupt (well, in fact by a signal such as either SIGINT, - SIGTERM or SIGHUP). The execution of postfunc() is protected - against keyboard interrupts and is guaranteed to run to - completion.""" - self._setup_sig_handler() - try: - self.job.start() - finally: - postfunc() - self._reset_sig_handler() - - def were_interrupted(self): - """Returns whether the jobs were interrupted by a signal.""" - return self.job.interrupted() - - def _setup_sig_handler(self): - """Setup an interrupt handler so that SCons can shutdown cleanly in - various conditions: - - a) SIGINT: Keyboard interrupt - b) SIGTERM: kill or system shutdown - c) SIGHUP: Controlling shell exiting - - We handle all of these cases by stopping the taskmaster. It - turns out that it very difficult to stop the build process - by throwing asynchronously an exception such as - KeyboardInterrupt. For example, the python Condition - variables (threading.Condition) and queue's do not seem to - asynchronous-exception-safe. It would require adding a whole - bunch of try/finally block and except KeyboardInterrupt all - over the place. - - Note also that we have to be careful to handle the case when - SCons forks before executing another process. In that case, we - want the child to exit immediately. - """ - def handler(signum, stack, self=self, parentpid=os.getpid()): - if os.getpid() == parentpid: - self.job.taskmaster.stop() - self.job.interrupted.set() - else: - os._exit(2) - - self.old_sigint = signal.signal(signal.SIGINT, handler) - self.old_sigterm = signal.signal(signal.SIGTERM, handler) - try: - self.old_sighup = signal.signal(signal.SIGHUP, handler) - except AttributeError: - pass - - def _reset_sig_handler(self): - """Restore the signal handlers to their previous state (before the - call to _setup_sig_handler().""" - - signal.signal(signal.SIGINT, self.old_sigint) - signal.signal(signal.SIGTERM, self.old_sigterm) - try: - signal.signal(signal.SIGHUP, self.old_sighup) - except AttributeError: - pass - -class Serial(object): - """This class is used to execute tasks in series, and is more efficient - than Parallel, but is only appropriate for non-parallel builds. Only - one instance of this class should be in existence at a time. - - This class is not thread safe. - """ - - def __init__(self, taskmaster): - """Create a new serial job given a taskmaster. - - The taskmaster's next_task() method should return the next task - that needs to be executed, or None if there are no more tasks. The - taskmaster's executed() method will be called for each task when it - is successfully executed or failed() will be called if it failed to - execute (e.g. execute() raised an exception).""" - - self.taskmaster = taskmaster - self.interrupted = InterruptState() - - def start(self): - """Start the job. This will begin pulling tasks from the taskmaster - and executing them, and return when there are no more tasks. If a task - fails to execute (i.e. execute() raises an exception), then the job will - stop.""" - - while True: - task = self.taskmaster.next_task() - - if task is None: - break - - try: - task.prepare() - if task.needs_execute(): - task.execute() - except: - if self.interrupted(): - try: - raise SCons.Errors.BuildError( - task.targets[0], errstr=interrupt_msg) - except: - task.exception_set() - else: - task.exception_set() - - # Let the failed() callback function arrange for the - # build to stop if that's appropriate. - task.failed() - else: - task.executed() - - task.postprocess() - self.taskmaster.cleanup() - - -# Trap import failure so that everything in the Job module but the -# Parallel class (and its dependent classes) will work if the interpreter -# doesn't support threads. -try: - import queue - import threading -except ImportError: - pass -else: - class Worker(threading.Thread): - """A worker thread waits on a task to be posted to its request queue, - dequeues the task, executes it, and posts a tuple including the task - and a boolean indicating whether the task executed successfully. """ - - def __init__(self, requestQueue, resultsQueue, interrupted): - threading.Thread.__init__(self) - self.setDaemon(1) - self.requestQueue = requestQueue - self.resultsQueue = resultsQueue - self.interrupted = interrupted - self.start() - - def run(self): - while True: - task = self.requestQueue.get() - - if task is None: - # The "None" value is used as a sentinel by - # ThreadPool.cleanup(). This indicates that there - # are no more tasks, so we should quit. - break - - try: - if self.interrupted(): - raise SCons.Errors.BuildError( - task.targets[0], errstr=interrupt_msg) - task.execute() - except: - task.exception_set() - ok = False - else: - ok = True - - self.resultsQueue.put((task, ok)) - - class ThreadPool(object): - """This class is responsible for spawning and managing worker threads.""" - - def __init__(self, num, stack_size, interrupted): - """Create the request and reply queues, and 'num' worker threads. - - One must specify the stack size of the worker threads. The - stack size is specified in kilobytes. - """ - self.requestQueue = queue.Queue(0) - self.resultsQueue = queue.Queue(0) - - try: - prev_size = threading.stack_size(stack_size*1024) - except AttributeError, e: - # Only print a warning if the stack size has been - # explicitly set. - if not explicit_stack_size is None: - msg = "Setting stack size is unsupported by this version of Python:\n " + \ - e.args[0] - SCons.Warnings.warn(SCons.Warnings.StackSizeWarning, msg) - except ValueError, e: - msg = "Setting stack size failed:\n " + str(e) - SCons.Warnings.warn(SCons.Warnings.StackSizeWarning, msg) - - # Create worker threads - self.workers = [] - for _ in range(num): - worker = Worker(self.requestQueue, self.resultsQueue, interrupted) - self.workers.append(worker) - - if 'prev_size' in locals(): - threading.stack_size(prev_size) - - def put(self, task): - """Put task into request queue.""" - self.requestQueue.put(task) - - def get(self): - """Remove and return a result tuple from the results queue.""" - return self.resultsQueue.get() - - def preparation_failed(self, task): - self.resultsQueue.put((task, False)) - - def cleanup(self): - """ - Shuts down the thread pool, giving each worker thread a - chance to shut down gracefully. - """ - # For each worker thread, put a sentinel "None" value - # on the requestQueue (indicating that there's no work - # to be done) so that each worker thread will get one and - # terminate gracefully. - for _ in self.workers: - self.requestQueue.put(None) - - # Wait for all of the workers to terminate. - # - # If we don't do this, later Python versions (2.4, 2.5) often - # seem to raise exceptions during shutdown. This happens - # in requestQueue.get(), as an assertion failure that - # requestQueue.not_full is notified while not acquired, - # seemingly because the main thread has shut down (or is - # in the process of doing so) while the workers are still - # trying to pull sentinels off the requestQueue. - # - # Normally these terminations should happen fairly quickly, - # but we'll stick a one-second timeout on here just in case - # someone gets hung. - for worker in self.workers: - worker.join(1.0) - self.workers = [] - - class Parallel(object): - """This class is used to execute tasks in parallel, and is somewhat - less efficient than Serial, but is appropriate for parallel builds. - - This class is thread safe. - """ - - def __init__(self, taskmaster, num, stack_size): - """Create a new parallel job given a taskmaster. - - The taskmaster's next_task() method should return the next - task that needs to be executed, or None if there are no more - tasks. The taskmaster's executed() method will be called - for each task when it is successfully executed or failed() - will be called if the task failed to execute (i.e. execute() - raised an exception). - - Note: calls to taskmaster are serialized, but calls to - execute() on distinct tasks are not serialized, because - that is the whole point of parallel jobs: they can execute - multiple tasks simultaneously. """ - - self.taskmaster = taskmaster - self.interrupted = InterruptState() - self.tp = ThreadPool(num, stack_size, self.interrupted) - - self.maxjobs = num - - def start(self): - """Start the job. This will begin pulling tasks from the - taskmaster and executing them, and return when there are no - more tasks. If a task fails to execute (i.e. execute() raises - an exception), then the job will stop.""" - - jobs = 0 - - while True: - # Start up as many available tasks as we're - # allowed to. - while jobs < self.maxjobs: - task = self.taskmaster.next_task() - if task is None: - break - - try: - # prepare task for execution - task.prepare() - except: - task.exception_set() - task.failed() - task.postprocess() - else: - if task.needs_execute(): - # dispatch task - self.tp.put(task) - jobs = jobs + 1 - else: - task.executed() - task.postprocess() - - if not task and not jobs: break - - # Let any/all completed tasks finish up before we go - # back and put the next batch of tasks on the queue. - while True: - task, ok = self.tp.get() - jobs = jobs - 1 - - if ok: - task.executed() - else: - if self.interrupted(): - try: - raise SCons.Errors.BuildError( - task.targets[0], errstr=interrupt_msg) - except: - task.exception_set() - - # Let the failed() callback function arrange - # for the build to stop if that's appropriate. - task.failed() - - task.postprocess() - - if self.tp.resultsQueue.empty(): - break - - self.tp.cleanup() - self.taskmaster.cleanup() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Memoize.py b/scons/scons-local-2.2.0/SCons/Memoize.py deleted file mode 100644 index 9850a841f..000000000 --- a/scons/scons-local-2.2.0/SCons/Memoize.py +++ /dev/null @@ -1,244 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Memoize.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -__doc__ = """Memoizer - -A metaclass implementation to count hits and misses of the computed -values that various methods cache in memory. - -Use of this modules assumes that wrapped methods be coded to cache their -values in a consistent way. Here is an example of wrapping a method -that returns a computed value, with no input parameters: - - memoizer_counters = [] # Memoization - - memoizer_counters.append(SCons.Memoize.CountValue('foo')) # Memoization - - def foo(self): - - try: # Memoization - return self._memo['foo'] # Memoization - except KeyError: # Memoization - pass # Memoization - - result = self.compute_foo_value() - - self._memo['foo'] = result # Memoization - - return result - -Here is an example of wrapping a method that will return different values -based on one or more input arguments: - - def _bar_key(self, argument): # Memoization - return argument # Memoization - - memoizer_counters.append(SCons.Memoize.CountDict('bar', _bar_key)) # Memoization - - def bar(self, argument): - - memo_key = argument # Memoization - try: # Memoization - memo_dict = self._memo['bar'] # Memoization - except KeyError: # Memoization - memo_dict = {} # Memoization - self._memo['dict'] = memo_dict # Memoization - else: # Memoization - try: # Memoization - return memo_dict[memo_key] # Memoization - except KeyError: # Memoization - pass # Memoization - - result = self.compute_bar_value(argument) - - memo_dict[memo_key] = result # Memoization - - return result - -At one point we avoided replicating this sort of logic in all the methods -by putting it right into this module, but we've moved away from that at -present (see the "Historical Note," below.). - -Deciding what to cache is tricky, because different configurations -can have radically different performance tradeoffs, and because the -tradeoffs involved are often so non-obvious. Consequently, deciding -whether or not to cache a given method will likely be more of an art than -a science, but should still be based on available data from this module. -Here are some VERY GENERAL guidelines about deciding whether or not to -cache return values from a method that's being called a lot: - - -- The first question to ask is, "Can we change the calling code - so this method isn't called so often?" Sometimes this can be - done by changing the algorithm. Sometimes the *caller* should - be memoized, not the method you're looking at. - - -- The memoized function should be timed with multiple configurations - to make sure it doesn't inadvertently slow down some other - configuration. - - -- When memoizing values based on a dictionary key composed of - input arguments, you don't need to use all of the arguments - if some of them don't affect the return values. - -Historical Note: The initial Memoizer implementation actually handled -the caching of values for the wrapped methods, based on a set of generic -algorithms for computing hashable values based on the method's arguments. -This collected caching logic nicely, but had two drawbacks: - - Running arguments through a generic key-conversion mechanism is slower - (and less flexible) than just coding these things directly. Since the - methods that need memoized values are generally performance-critical, - slowing them down in order to collect the logic isn't the right - tradeoff. - - Use of the memoizer really obscured what was being called, because - all the memoized methods were wrapped with re-used generic methods. - This made it more difficult, for example, to use the Python profiler - to figure out how to optimize the underlying methods. -""" - -import types - -# A flag controlling whether or not we actually use memoization. -use_memoizer = None - -CounterList = [] - -class Counter(object): - """ - Base class for counting memoization hits and misses. - - We expect that the metaclass initialization will have filled in - the .name attribute that represents the name of the function - being counted. - """ - def __init__(self, method_name): - """ - """ - self.method_name = method_name - self.hit = 0 - self.miss = 0 - CounterList.append(self) - def display(self): - fmt = " %7d hits %7d misses %s()" - print fmt % (self.hit, self.miss, self.name) - def __cmp__(self, other): - try: - return cmp(self.name, other.name) - except AttributeError: - return 0 - -class CountValue(Counter): - """ - A counter class for simple, atomic memoized values. - - A CountValue object should be instantiated in a class for each of - the class's methods that memoizes its return value by simply storing - the return value in its _memo dictionary. - - We expect that the metaclass initialization will fill in the - .underlying_method attribute with the method that we're wrapping. - We then call the underlying_method method after counting whether - its memoized value has already been set (a hit) or not (a miss). - """ - def __call__(self, *args, **kw): - obj = args[0] - if self.method_name in obj._memo: - self.hit = self.hit + 1 - else: - self.miss = self.miss + 1 - return self.underlying_method(*args, **kw) - -class CountDict(Counter): - """ - A counter class for memoized values stored in a dictionary, with - keys based on the method's input arguments. - - A CountDict object is instantiated in a class for each of the - class's methods that memoizes its return value in a dictionary, - indexed by some key that can be computed from one or more of - its input arguments. - - We expect that the metaclass initialization will fill in the - .underlying_method attribute with the method that we're wrapping. - We then call the underlying_method method after counting whether the - computed key value is already present in the memoization dictionary - (a hit) or not (a miss). - """ - def __init__(self, method_name, keymaker): - """ - """ - Counter.__init__(self, method_name) - self.keymaker = keymaker - def __call__(self, *args, **kw): - obj = args[0] - try: - memo_dict = obj._memo[self.method_name] - except KeyError: - self.miss = self.miss + 1 - else: - key = self.keymaker(*args, **kw) - if key in memo_dict: - self.hit = self.hit + 1 - else: - self.miss = self.miss + 1 - return self.underlying_method(*args, **kw) - -class Memoizer(object): - """Object which performs caching of method calls for its 'primary' - instance.""" - - def __init__(self): - pass - -def Dump(title=None): - if title: - print title - CounterList.sort() - for counter in CounterList: - counter.display() - -class Memoized_Metaclass(type): - def __init__(cls, name, bases, cls_dict): - super(Memoized_Metaclass, cls).__init__(name, bases, cls_dict) - - for counter in cls_dict.get('memoizer_counters', []): - method_name = counter.method_name - - counter.name = cls.__name__ + '.' + method_name - counter.underlying_method = cls_dict[method_name] - - replacement_method = types.MethodType(counter, None, cls) - setattr(cls, method_name, replacement_method) - -def EnableMemoization(): - global use_memoizer - use_memoizer = 1 - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Node/Alias.py b/scons/scons-local-2.2.0/SCons/Node/Alias.py deleted file mode 100644 index fbef1fdd1..000000000 --- a/scons/scons-local-2.2.0/SCons/Node/Alias.py +++ /dev/null @@ -1,152 +0,0 @@ - -"""scons.Node.Alias - -Alias nodes. - -This creates a hash of global Aliases (dummy targets). - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Node/Alias.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import collections - -import SCons.Errors -import SCons.Node -import SCons.Util - -class AliasNameSpace(collections.UserDict): - def Alias(self, name, **kw): - if isinstance(name, SCons.Node.Alias.Alias): - return name - try: - a = self[name] - except KeyError: - a = SCons.Node.Alias.Alias(name, **kw) - self[name] = a - return a - - def lookup(self, name, **kw): - try: - return self[name] - except KeyError: - return None - -class AliasNodeInfo(SCons.Node.NodeInfoBase): - current_version_id = 1 - field_list = ['csig'] - def str_to_node(self, s): - return default_ans.Alias(s) - -class AliasBuildInfo(SCons.Node.BuildInfoBase): - current_version_id = 1 - -class Alias(SCons.Node.Node): - - NodeInfo = AliasNodeInfo - BuildInfo = AliasBuildInfo - - def __init__(self, name): - SCons.Node.Node.__init__(self) - self.name = name - - def str_for_display(self): - return '"' + self.__str__() + '"' - - def __str__(self): - return self.name - - def make_ready(self): - self.get_csig() - - really_build = SCons.Node.Node.build - is_up_to_date = SCons.Node.Node.children_are_up_to_date - - def is_under(self, dir): - # Make Alias nodes get built regardless of - # what directory scons was run from. Alias nodes - # are outside the filesystem: - return 1 - - def get_contents(self): - """The contents of an alias is the concatenation - of the content signatures of all its sources.""" - childsigs = [n.get_csig() for n in self.children()] - return ''.join(childsigs) - - def sconsign(self): - """An Alias is not recorded in .sconsign files""" - pass - - # - # - # - - def changed_since_last_build(self, target, prev_ni): - cur_csig = self.get_csig() - try: - return cur_csig != prev_ni.csig - except AttributeError: - return 1 - - def build(self): - """A "builder" for aliases.""" - pass - - def convert(self): - try: del self.builder - except AttributeError: pass - self.reset_executor() - self.build = self.really_build - - def get_csig(self): - """ - Generate a node's content signature, the digested signature - of its content. - - node - the node - cache - alternate node to use for the signature cache - returns - the content signature - """ - try: - return self.ninfo.csig - except AttributeError: - pass - - contents = self.get_contents() - csig = SCons.Util.MD5signature(contents) - self.get_ninfo().csig = csig - return csig - -default_ans = AliasNameSpace() - -SCons.Node.arg2nodes_lookups.append(default_ans.lookup) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Node/FS.py b/scons/scons-local-2.2.0/SCons/Node/FS.py deleted file mode 100644 index 3f3cf2870..000000000 --- a/scons/scons-local-2.2.0/SCons/Node/FS.py +++ /dev/null @@ -1,3302 +0,0 @@ -"""scons.Node.FS - -File system nodes. - -These Nodes represent the canonical external objects that people think -of when they think of building software: files and directories. - -This holds a "default_fs" variable that should be initialized with an FS -that can be used by scripts or modules looking for the canonical default. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Node/FS.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import fnmatch -import os -import re -import shutil -import stat -import sys -import time -import codecs - -import SCons.Action -from SCons.Debug import logInstanceCreation -import SCons.Errors -import SCons.Memoize -import SCons.Node -import SCons.Node.Alias -import SCons.Subst -import SCons.Util -import SCons.Warnings - -from SCons.Debug import Trace - -do_store_info = True -print_duplicate = 0 - - -class EntryProxyAttributeError(AttributeError): - """ - An AttributeError subclass for recording and displaying the name - of the underlying Entry involved in an AttributeError exception. - """ - def __init__(self, entry_proxy, attribute): - AttributeError.__init__(self) - self.entry_proxy = entry_proxy - self.attribute = attribute - def __str__(self): - entry = self.entry_proxy.get() - fmt = "%s instance %s has no attribute %s" - return fmt % (entry.__class__.__name__, - repr(entry.name), - repr(self.attribute)) - -# The max_drift value: by default, use a cached signature value for -# any file that's been untouched for more than two days. -default_max_drift = 2*24*60*60 - -# -# We stringify these file system Nodes a lot. Turning a file system Node -# into a string is non-trivial, because the final string representation -# can depend on a lot of factors: whether it's a derived target or not, -# whether it's linked to a repository or source directory, and whether -# there's duplication going on. The normal technique for optimizing -# calculations like this is to memoize (cache) the string value, so you -# only have to do the calculation once. -# -# A number of the above factors, however, can be set after we've already -# been asked to return a string for a Node, because a Repository() or -# VariantDir() call or the like may not occur until later in SConscript -# files. So this variable controls whether we bother trying to save -# string values for Nodes. The wrapper interface can set this whenever -# they're done mucking with Repository and VariantDir and the other stuff, -# to let this module know it can start returning saved string values -# for Nodes. -# -Save_Strings = None - -def save_strings(val): - global Save_Strings - Save_Strings = val - -# -# Avoid unnecessary function calls by recording a Boolean value that -# tells us whether or not os.path.splitdrive() actually does anything -# on this system, and therefore whether we need to bother calling it -# when looking up path names in various methods below. -# - -do_splitdrive = None -_my_splitdrive =None - -def initialize_do_splitdrive(): - global do_splitdrive - global has_unc - drive, path = os.path.splitdrive('X:/foo') - has_unc = hasattr(os.path, 'splitunc') - - do_splitdrive = not not drive or has_unc - - global _my_splitdrive - if has_unc: - def splitdrive(p): - if p[1:2] == ':': - return p[:2], p[2:] - if p[0:2] == '//': - # Note that we leave a leading slash in the path - # because UNC paths are always absolute. - return '//', p[1:] - return '', p - else: - def splitdrive(p): - if p[1:2] == ':': - return p[:2], p[2:] - return '', p - _my_splitdrive = splitdrive - - # Keep some commonly used values in global variables to skip to - # module look-up costs. - global OS_SEP - global UNC_PREFIX - global os_sep_is_slash - - OS_SEP = os.sep - UNC_PREFIX = OS_SEP + OS_SEP - os_sep_is_slash = OS_SEP == '/' - -initialize_do_splitdrive() - -# Used to avoid invoking os.path.normpath if not necessary. -needs_normpath_check = re.compile( - r''' - # We need to renormalize the path if it contains any consecutive - # '/' characters. - .*// | - - # We need to renormalize the path if it contains a '..' directory. - # Note that we check for all the following cases: - # - # a) The path is a single '..' - # b) The path starts with '..'. E.g. '../' or '../moredirs' - # but we not match '..abc/'. - # c) The path ends with '..'. E.g. '/..' or 'dirs/..' - # d) The path contains a '..' in the middle. - # E.g. dirs/../moredirs - - (.*/)?\.\.(?:/|$) | - - # We need to renormalize the path if it contains a '.' - # directory, but NOT if it is a single '.' '/' characters. We - # do not want to match a single '.' because this case is checked - # for explicitely since this is common enough case. - # - # Note that we check for all the following cases: - # - # a) We don't match a single '.' - # b) We match if the path starts with '.'. E.g. './' or - # './moredirs' but we not match '.abc/'. - # c) We match if the path ends with '.'. E.g. '/.' or - # 'dirs/.' - # d) We match if the path contains a '.' in the middle. - # E.g. dirs/./moredirs - - \./|.*/\.(?:/|$) - - ''', - re.VERBOSE - ) -needs_normpath_match = needs_normpath_check.match - -# -# SCons.Action objects for interacting with the outside world. -# -# The Node.FS methods in this module should use these actions to -# create and/or remove files and directories; they should *not* use -# os.{link,symlink,unlink,mkdir}(), etc., directly. -# -# Using these SCons.Action objects ensures that descriptions of these -# external activities are properly displayed, that the displays are -# suppressed when the -s (silent) option is used, and (most importantly) -# the actions are disabled when the the -n option is used, in which case -# there should be *no* changes to the external file system(s)... -# - -if hasattr(os, 'link'): - def _hardlink_func(fs, src, dst): - # If the source is a symlink, we can't just hard-link to it - # because a relative symlink may point somewhere completely - # different. We must disambiguate the symlink and then - # hard-link the final destination file. - while fs.islink(src): - link = fs.readlink(src) - if not os.path.isabs(link): - src = link - else: - src = os.path.join(os.path.dirname(src), link) - fs.link(src, dst) -else: - _hardlink_func = None - -if hasattr(os, 'symlink'): - def _softlink_func(fs, src, dst): - fs.symlink(src, dst) -else: - _softlink_func = None - -def _copy_func(fs, src, dest): - shutil.copy2(src, dest) - st = fs.stat(src) - fs.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) - - -Valid_Duplicates = ['hard-soft-copy', 'soft-hard-copy', - 'hard-copy', 'soft-copy', 'copy'] - -Link_Funcs = [] # contains the callables of the specified duplication style - -def set_duplicate(duplicate): - # Fill in the Link_Funcs list according to the argument - # (discarding those not available on the platform). - - # Set up the dictionary that maps the argument names to the - # underlying implementations. We do this inside this function, - # not in the top-level module code, so that we can remap os.link - # and os.symlink for testing purposes. - link_dict = { - 'hard' : _hardlink_func, - 'soft' : _softlink_func, - 'copy' : _copy_func - } - - if not duplicate in Valid_Duplicates: - raise SCons.Errors.InternalError("The argument of set_duplicate " - "should be in Valid_Duplicates") - global Link_Funcs - Link_Funcs = [] - for func in duplicate.split('-'): - if link_dict[func]: - Link_Funcs.append(link_dict[func]) - -def LinkFunc(target, source, env): - # Relative paths cause problems with symbolic links, so - # we use absolute paths, which may be a problem for people - # who want to move their soft-linked src-trees around. Those - # people should use the 'hard-copy' mode, softlinks cannot be - # used for that; at least I have no idea how ... - src = source[0].abspath - dest = target[0].abspath - dir, file = os.path.split(dest) - if dir and not target[0].fs.isdir(dir): - os.makedirs(dir) - if not Link_Funcs: - # Set a default order of link functions. - set_duplicate('hard-soft-copy') - fs = source[0].fs - # Now link the files with the previously specified order. - for func in Link_Funcs: - try: - func(fs, src, dest) - break - except (IOError, OSError): - # An OSError indicates something happened like a permissions - # problem or an attempt to symlink across file-system - # boundaries. An IOError indicates something like the file - # not existing. In either case, keeping trying additional - # functions in the list and only raise an error if the last - # one failed. - if func == Link_Funcs[-1]: - # exception of the last link method (copy) are fatal - raise - return 0 - -Link = SCons.Action.Action(LinkFunc, None) -def LocalString(target, source, env): - return 'Local copy of %s from %s' % (target[0], source[0]) - -LocalCopy = SCons.Action.Action(LinkFunc, LocalString) - -def UnlinkFunc(target, source, env): - t = target[0] - t.fs.unlink(t.abspath) - return 0 - -Unlink = SCons.Action.Action(UnlinkFunc, None) - -def MkdirFunc(target, source, env): - t = target[0] - if not t.exists(): - t.fs.mkdir(t.abspath) - return 0 - -Mkdir = SCons.Action.Action(MkdirFunc, None, presub=None) - -MkdirBuilder = None - -def get_MkdirBuilder(): - global MkdirBuilder - if MkdirBuilder is None: - import SCons.Builder - import SCons.Defaults - # "env" will get filled in by Executor.get_build_env() - # calling SCons.Defaults.DefaultEnvironment() when necessary. - MkdirBuilder = SCons.Builder.Builder(action = Mkdir, - env = None, - explain = None, - is_explicit = None, - target_scanner = SCons.Defaults.DirEntryScanner, - name = "MkdirBuilder") - return MkdirBuilder - -class _Null(object): - pass - -_null = _Null() - -DefaultSCCSBuilder = None -DefaultRCSBuilder = None - -def get_DefaultSCCSBuilder(): - global DefaultSCCSBuilder - if DefaultSCCSBuilder is None: - import SCons.Builder - # "env" will get filled in by Executor.get_build_env() - # calling SCons.Defaults.DefaultEnvironment() when necessary. - act = SCons.Action.Action('$SCCSCOM', '$SCCSCOMSTR') - DefaultSCCSBuilder = SCons.Builder.Builder(action = act, - env = None, - name = "DefaultSCCSBuilder") - return DefaultSCCSBuilder - -def get_DefaultRCSBuilder(): - global DefaultRCSBuilder - if DefaultRCSBuilder is None: - import SCons.Builder - # "env" will get filled in by Executor.get_build_env() - # calling SCons.Defaults.DefaultEnvironment() when necessary. - act = SCons.Action.Action('$RCS_COCOM', '$RCS_COCOMSTR') - DefaultRCSBuilder = SCons.Builder.Builder(action = act, - env = None, - name = "DefaultRCSBuilder") - return DefaultRCSBuilder - -# Cygwin's os.path.normcase pretends it's on a case-sensitive filesystem. -_is_cygwin = sys.platform == "cygwin" -if os.path.normcase("TeSt") == os.path.normpath("TeSt") and not _is_cygwin: - def _my_normcase(x): - return x -else: - def _my_normcase(x): - return x.upper() - - - -class DiskChecker(object): - def __init__(self, type, do, ignore): - self.type = type - self.do = do - self.ignore = ignore - self.func = do - def __call__(self, *args, **kw): - return self.func(*args, **kw) - def set(self, list): - if self.type in list: - self.func = self.do - else: - self.func = self.ignore - -def do_diskcheck_match(node, predicate, errorfmt): - result = predicate() - try: - # If calling the predicate() cached a None value from stat(), - # remove it so it doesn't interfere with later attempts to - # build this Node as we walk the DAG. (This isn't a great way - # to do this, we're reaching into an interface that doesn't - # really belong to us, but it's all about performance, so - # for now we'll just document the dependency...) - if node._memo['stat'] is None: - del node._memo['stat'] - except (AttributeError, KeyError): - pass - if result: - raise TypeError(errorfmt % node.abspath) - -def ignore_diskcheck_match(node, predicate, errorfmt): - pass - -def do_diskcheck_rcs(node, name): - try: - rcs_dir = node.rcs_dir - except AttributeError: - if node.entry_exists_on_disk('RCS'): - rcs_dir = node.Dir('RCS') - else: - rcs_dir = None - node.rcs_dir = rcs_dir - if rcs_dir: - return rcs_dir.entry_exists_on_disk(name+',v') - return None - -def ignore_diskcheck_rcs(node, name): - return None - -def do_diskcheck_sccs(node, name): - try: - sccs_dir = node.sccs_dir - except AttributeError: - if node.entry_exists_on_disk('SCCS'): - sccs_dir = node.Dir('SCCS') - else: - sccs_dir = None - node.sccs_dir = sccs_dir - if sccs_dir: - return sccs_dir.entry_exists_on_disk('s.'+name) - return None - -def ignore_diskcheck_sccs(node, name): - return None - -diskcheck_match = DiskChecker('match', do_diskcheck_match, ignore_diskcheck_match) -diskcheck_rcs = DiskChecker('rcs', do_diskcheck_rcs, ignore_diskcheck_rcs) -diskcheck_sccs = DiskChecker('sccs', do_diskcheck_sccs, ignore_diskcheck_sccs) - -diskcheckers = [ - diskcheck_match, - diskcheck_rcs, - diskcheck_sccs, -] - -def set_diskcheck(list): - for dc in diskcheckers: - dc.set(list) - -def diskcheck_types(): - return [dc.type for dc in diskcheckers] - - - -class EntryProxy(SCons.Util.Proxy): - - __str__ = SCons.Util.Delegate('__str__') - - def __get_abspath(self): - entry = self.get() - return SCons.Subst.SpecialAttrWrapper(entry.get_abspath(), - entry.name + "_abspath") - - def __get_filebase(self): - name = self.get().name - return SCons.Subst.SpecialAttrWrapper(SCons.Util.splitext(name)[0], - name + "_filebase") - - def __get_suffix(self): - name = self.get().name - return SCons.Subst.SpecialAttrWrapper(SCons.Util.splitext(name)[1], - name + "_suffix") - - def __get_file(self): - name = self.get().name - return SCons.Subst.SpecialAttrWrapper(name, name + "_file") - - def __get_base_path(self): - """Return the file's directory and file name, with the - suffix stripped.""" - entry = self.get() - return SCons.Subst.SpecialAttrWrapper(SCons.Util.splitext(entry.get_path())[0], - entry.name + "_base") - - def __get_posix_path(self): - """Return the path with / as the path separator, - regardless of platform.""" - if os_sep_is_slash: - return self - else: - entry = self.get() - r = entry.get_path().replace(OS_SEP, '/') - return SCons.Subst.SpecialAttrWrapper(r, entry.name + "_posix") - - def __get_windows_path(self): - """Return the path with \ as the path separator, - regardless of platform.""" - if OS_SEP == '\\': - return self - else: - entry = self.get() - r = entry.get_path().replace(OS_SEP, '\\') - return SCons.Subst.SpecialAttrWrapper(r, entry.name + "_windows") - - def __get_srcnode(self): - return EntryProxy(self.get().srcnode()) - - def __get_srcdir(self): - """Returns the directory containing the source node linked to this - node via VariantDir(), or the directory of this node if not linked.""" - return EntryProxy(self.get().srcnode().dir) - - def __get_rsrcnode(self): - return EntryProxy(self.get().srcnode().rfile()) - - def __get_rsrcdir(self): - """Returns the directory containing the source node linked to this - node via VariantDir(), or the directory of this node if not linked.""" - return EntryProxy(self.get().srcnode().rfile().dir) - - def __get_dir(self): - return EntryProxy(self.get().dir) - - dictSpecialAttrs = { "base" : __get_base_path, - "posix" : __get_posix_path, - "windows" : __get_windows_path, - "win32" : __get_windows_path, - "srcpath" : __get_srcnode, - "srcdir" : __get_srcdir, - "dir" : __get_dir, - "abspath" : __get_abspath, - "filebase" : __get_filebase, - "suffix" : __get_suffix, - "file" : __get_file, - "rsrcpath" : __get_rsrcnode, - "rsrcdir" : __get_rsrcdir, - } - - def __getattr__(self, name): - # This is how we implement the "special" attributes - # such as base, posix, srcdir, etc. - try: - attr_function = self.dictSpecialAttrs[name] - except KeyError: - try: - attr = SCons.Util.Proxy.__getattr__(self, name) - except AttributeError, e: - # Raise our own AttributeError subclass with an - # overridden __str__() method that identifies the - # name of the entry that caused the exception. - raise EntryProxyAttributeError(self, name) - return attr - else: - return attr_function(self) - -class Base(SCons.Node.Node): - """A generic class for file system entries. This class is for - when we don't know yet whether the entry being looked up is a file - or a directory. Instances of this class can morph into either - Dir or File objects by a later, more precise lookup. - - Note: this class does not define __cmp__ and __hash__ for - efficiency reasons. SCons does a lot of comparing of - Node.FS.{Base,Entry,File,Dir} objects, so those operations must be - as fast as possible, which means we want to use Python's built-in - object identity comparisons. - """ - - memoizer_counters = [] - - def __init__(self, name, directory, fs): - """Initialize a generic Node.FS.Base object. - - Call the superclass initialization, take care of setting up - our relative and absolute paths, identify our parent - directory, and indicate that this node should use - signatures.""" - if __debug__: logInstanceCreation(self, 'Node.FS.Base') - SCons.Node.Node.__init__(self) - - # Filenames and paths are probably reused and are intern'ed to - # save some memory. - - #: Filename with extension as it was specified when the object was - #: created; to obtain filesystem path, use Python str() function - self.name = SCons.Util.silent_intern(name) - #: Cached filename extension - self.suffix = SCons.Util.silent_intern(SCons.Util.splitext(name)[1]) - self.fs = fs #: Reference to parent Node.FS object - - assert directory, "A directory must be provided" - - self.abspath = SCons.Util.silent_intern(directory.entry_abspath(name)) - self.labspath = SCons.Util.silent_intern(directory.entry_labspath(name)) - if directory.path == '.': - self.path = SCons.Util.silent_intern(name) - else: - self.path = SCons.Util.silent_intern(directory.entry_path(name)) - if directory.tpath == '.': - self.tpath = SCons.Util.silent_intern(name) - else: - self.tpath = SCons.Util.silent_intern(directory.entry_tpath(name)) - self.path_elements = directory.path_elements + [self] - - self.dir = directory - self.cwd = None # will hold the SConscript directory for target nodes - self.duplicate = directory.duplicate - - def str_for_display(self): - return '"' + self.__str__() + '"' - - def must_be_same(self, klass): - """ - This node, which already existed, is being looked up as the - specified klass. Raise an exception if it isn't. - """ - if isinstance(self, klass) or klass is Entry: - return - raise TypeError("Tried to lookup %s '%s' as a %s." %\ - (self.__class__.__name__, self.path, klass.__name__)) - - def get_dir(self): - return self.dir - - def get_suffix(self): - return self.suffix - - def rfile(self): - return self - - def __str__(self): - """A Node.FS.Base object's string representation is its path - name.""" - global Save_Strings - if Save_Strings: - return self._save_str() - return self._get_str() - - memoizer_counters.append(SCons.Memoize.CountValue('_save_str')) - - def _save_str(self): - try: - return self._memo['_save_str'] - except KeyError: - pass - result = sys.intern(self._get_str()) - self._memo['_save_str'] = result - return result - - def _get_str(self): - global Save_Strings - if self.duplicate or self.is_derived(): - return self.get_path() - srcnode = self.srcnode() - if srcnode.stat() is None and self.stat() is not None: - result = self.get_path() - else: - result = srcnode.get_path() - if not Save_Strings: - # We're not at the point where we're saving the string - # representations of FS Nodes (because we haven't finished - # reading the SConscript files and need to have str() return - # things relative to them). That also means we can't yet - # cache values returned (or not returned) by stat(), since - # Python code in the SConscript files might still create - # or otherwise affect the on-disk file. So get rid of the - # values that the underlying stat() method saved. - try: del self._memo['stat'] - except KeyError: pass - if self is not srcnode: - try: del srcnode._memo['stat'] - except KeyError: pass - return result - - rstr = __str__ - - memoizer_counters.append(SCons.Memoize.CountValue('stat')) - - def stat(self): - try: return self._memo['stat'] - except KeyError: pass - try: result = self.fs.stat(self.abspath) - except os.error: result = None - self._memo['stat'] = result - return result - - def exists(self): - return self.stat() is not None - - def rexists(self): - return self.rfile().exists() - - def getmtime(self): - st = self.stat() - if st: return st[stat.ST_MTIME] - else: return None - - def getsize(self): - st = self.stat() - if st: return st[stat.ST_SIZE] - else: return None - - def isdir(self): - st = self.stat() - return st is not None and stat.S_ISDIR(st[stat.ST_MODE]) - - def isfile(self): - st = self.stat() - return st is not None and stat.S_ISREG(st[stat.ST_MODE]) - - if hasattr(os, 'symlink'): - def islink(self): - try: st = self.fs.lstat(self.abspath) - except os.error: return 0 - return stat.S_ISLNK(st[stat.ST_MODE]) - else: - def islink(self): - return 0 # no symlinks - - def is_under(self, dir): - if self is dir: - return 1 - else: - return self.dir.is_under(dir) - - def set_local(self): - self._local = 1 - - def srcnode(self): - """If this node is in a build path, return the node - corresponding to its source file. Otherwise, return - ourself. - """ - srcdir_list = self.dir.srcdir_list() - if srcdir_list: - srcnode = srcdir_list[0].Entry(self.name) - srcnode.must_be_same(self.__class__) - return srcnode - return self - - def get_path(self, dir=None): - """Return path relative to the current working directory of the - Node.FS.Base object that owns us.""" - if not dir: - dir = self.fs.getcwd() - if self == dir: - return '.' - path_elems = self.path_elements - pathname = '' - try: i = path_elems.index(dir) - except ValueError: - for p in path_elems[:-1]: - pathname += p.dirname - else: - for p in path_elems[i+1:-1]: - pathname += p.dirname - return pathname + path_elems[-1].name - - def set_src_builder(self, builder): - """Set the source code builder for this node.""" - self.sbuilder = builder - if not self.has_builder(): - self.builder_set(builder) - - def src_builder(self): - """Fetch the source code builder for this node. - - If there isn't one, we cache the source code builder specified - for the directory (which in turn will cache the value from its - parent directory, and so on up to the file system root). - """ - try: - scb = self.sbuilder - except AttributeError: - scb = self.dir.src_builder() - self.sbuilder = scb - return scb - - def get_abspath(self): - """Get the absolute path of the file.""" - return self.abspath - - def for_signature(self): - # Return just our name. Even an absolute path would not work, - # because that can change thanks to symlinks or remapped network - # paths. - return self.name - - def get_subst_proxy(self): - try: - return self._proxy - except AttributeError: - ret = EntryProxy(self) - self._proxy = ret - return ret - - def target_from_source(self, prefix, suffix, splitext=SCons.Util.splitext): - """ - - Generates a target entry that corresponds to this entry (usually - a source file) with the specified prefix and suffix. - - Note that this method can be overridden dynamically for generated - files that need different behavior. See Tool/swig.py for - an example. - """ - return self.dir.Entry(prefix + splitext(self.name)[0] + suffix) - - def _Rfindalldirs_key(self, pathlist): - return pathlist - - memoizer_counters.append(SCons.Memoize.CountDict('Rfindalldirs', _Rfindalldirs_key)) - - def Rfindalldirs(self, pathlist): - """ - Return all of the directories for a given path list, including - corresponding "backing" directories in any repositories. - - The Node lookups are relative to this Node (typically a - directory), so memoizing result saves cycles from looking - up the same path for each target in a given directory. - """ - try: - memo_dict = self._memo['Rfindalldirs'] - except KeyError: - memo_dict = {} - self._memo['Rfindalldirs'] = memo_dict - else: - try: - return memo_dict[pathlist] - except KeyError: - pass - - create_dir_relative_to_self = self.Dir - result = [] - for path in pathlist: - if isinstance(path, SCons.Node.Node): - result.append(path) - else: - dir = create_dir_relative_to_self(path) - result.extend(dir.get_all_rdirs()) - - memo_dict[pathlist] = result - - return result - - def RDirs(self, pathlist): - """Search for a list of directories in the Repository list.""" - cwd = self.cwd or self.fs._cwd - return cwd.Rfindalldirs(pathlist) - - memoizer_counters.append(SCons.Memoize.CountValue('rentry')) - - def rentry(self): - try: - return self._memo['rentry'] - except KeyError: - pass - result = self - if not self.exists(): - norm_name = _my_normcase(self.name) - for dir in self.dir.get_all_rdirs(): - try: - node = dir.entries[norm_name] - except KeyError: - if dir.entry_exists_on_disk(self.name): - result = dir.Entry(self.name) - break - self._memo['rentry'] = result - return result - - def _glob1(self, pattern, ondisk=True, source=False, strings=False): - return [] - -class Entry(Base): - """This is the class for generic Node.FS entries--that is, things - that could be a File or a Dir, but we're just not sure yet. - Consequently, the methods in this class really exist just to - transform their associated object into the right class when the - time comes, and then call the same-named method in the transformed - class.""" - - def diskcheck_match(self): - pass - - def disambiguate(self, must_exist=None): - """ - """ - if self.isdir(): - self.__class__ = Dir - self._morph() - elif self.isfile(): - self.__class__ = File - self._morph() - self.clear() - else: - # There was nothing on-disk at this location, so look in - # the src directory. - # - # We can't just use self.srcnode() straight away because - # that would create an actual Node for this file in the src - # directory, and there might not be one. Instead, use the - # dir_on_disk() method to see if there's something on-disk - # with that name, in which case we can go ahead and call - # self.srcnode() to create the right type of entry. - srcdir = self.dir.srcnode() - if srcdir != self.dir and \ - srcdir.entry_exists_on_disk(self.name) and \ - self.srcnode().isdir(): - self.__class__ = Dir - self._morph() - elif must_exist: - msg = "No such file or directory: '%s'" % self.abspath - raise SCons.Errors.UserError(msg) - else: - self.__class__ = File - self._morph() - self.clear() - return self - - def rfile(self): - """We're a generic Entry, but the caller is actually looking for - a File at this point, so morph into one.""" - self.__class__ = File - self._morph() - self.clear() - return File.rfile(self) - - def scanner_key(self): - return self.get_suffix() - - def get_contents(self): - """Fetch the contents of the entry. Returns the exact binary - contents of the file.""" - try: - self = self.disambiguate(must_exist=1) - except SCons.Errors.UserError: - # There was nothing on disk with which to disambiguate - # this entry. Leave it as an Entry, but return a null - # string so calls to get_contents() in emitters and the - # like (e.g. in qt.py) don't have to disambiguate by hand - # or catch the exception. - return '' - else: - return self.get_contents() - - def get_text_contents(self): - """Fetch the decoded text contents of a Unicode encoded Entry. - - Since this should return the text contents from the file - system, we check to see into what sort of subclass we should - morph this Entry.""" - try: - self = self.disambiguate(must_exist=1) - except SCons.Errors.UserError: - # There was nothing on disk with which to disambiguate - # this entry. Leave it as an Entry, but return a null - # string so calls to get_text_contents() in emitters and - # the like (e.g. in qt.py) don't have to disambiguate by - # hand or catch the exception. - return '' - else: - return self.get_text_contents() - - def must_be_same(self, klass): - """Called to make sure a Node is a Dir. Since we're an - Entry, we can morph into one.""" - if self.__class__ is not klass: - self.__class__ = klass - self._morph() - self.clear() - - # The following methods can get called before the Taskmaster has - # had a chance to call disambiguate() directly to see if this Entry - # should really be a Dir or a File. We therefore use these to call - # disambiguate() transparently (from our caller's point of view). - # - # Right now, this minimal set of methods has been derived by just - # looking at some of the methods that will obviously be called early - # in any of the various Taskmasters' calling sequences, and then - # empirically figuring out which additional methods are necessary - # to make various tests pass. - - def exists(self): - """Return if the Entry exists. Check the file system to see - what we should turn into first. Assume a file if there's no - directory.""" - return self.disambiguate().exists() - - def rel_path(self, other): - d = self.disambiguate() - if d.__class__ is Entry: - raise Exception("rel_path() could not disambiguate File/Dir") - return d.rel_path(other) - - def new_ninfo(self): - return self.disambiguate().new_ninfo() - - def changed_since_last_build(self, target, prev_ni): - return self.disambiguate().changed_since_last_build(target, prev_ni) - - def _glob1(self, pattern, ondisk=True, source=False, strings=False): - return self.disambiguate()._glob1(pattern, ondisk, source, strings) - - def get_subst_proxy(self): - return self.disambiguate().get_subst_proxy() - -# This is for later so we can differentiate between Entry the class and Entry -# the method of the FS class. -_classEntry = Entry - - -class LocalFS(object): - - if SCons.Memoize.use_memoizer: - __metaclass__ = SCons.Memoize.Memoized_Metaclass - - # This class implements an abstraction layer for operations involving - # a local file system. Essentially, this wraps any function in - # the os, os.path or shutil modules that we use to actually go do - # anything with or to the local file system. - # - # Note that there's a very good chance we'll refactor this part of - # the architecture in some way as we really implement the interface(s) - # for remote file system Nodes. For example, the right architecture - # might be to have this be a subclass instead of a base class. - # Nevertheless, we're using this as a first step in that direction. - # - # We're not using chdir() yet because the calling subclass method - # needs to use os.chdir() directly to avoid recursion. Will we - # really need this one? - #def chdir(self, path): - # return os.chdir(path) - def chmod(self, path, mode): - return os.chmod(path, mode) - def copy(self, src, dst): - return shutil.copy(src, dst) - def copy2(self, src, dst): - return shutil.copy2(src, dst) - def exists(self, path): - return os.path.exists(path) - def getmtime(self, path): - return os.path.getmtime(path) - def getsize(self, path): - return os.path.getsize(path) - def isdir(self, path): - return os.path.isdir(path) - def isfile(self, path): - return os.path.isfile(path) - def link(self, src, dst): - return os.link(src, dst) - def lstat(self, path): - return os.lstat(path) - def listdir(self, path): - return os.listdir(path) - def makedirs(self, path): - return os.makedirs(path) - def mkdir(self, path): - return os.mkdir(path) - def rename(self, old, new): - return os.rename(old, new) - def stat(self, path): - return os.stat(path) - def symlink(self, src, dst): - return os.symlink(src, dst) - def open(self, path): - return open(path) - def unlink(self, path): - return os.unlink(path) - - if hasattr(os, 'symlink'): - def islink(self, path): - return os.path.islink(path) - else: - def islink(self, path): - return 0 # no symlinks - - if hasattr(os, 'readlink'): - def readlink(self, file): - return os.readlink(file) - else: - def readlink(self, file): - return '' - - -#class RemoteFS: -# # Skeleton for the obvious methods we might need from the -# # abstraction layer for a remote filesystem. -# def upload(self, local_src, remote_dst): -# pass -# def download(self, remote_src, local_dst): -# pass - - -class FS(LocalFS): - - memoizer_counters = [] - - def __init__(self, path = None): - """Initialize the Node.FS subsystem. - - The supplied path is the top of the source tree, where we - expect to find the top-level build file. If no path is - supplied, the current directory is the default. - - The path argument must be a valid absolute path. - """ - if __debug__: logInstanceCreation(self, 'Node.FS') - - self._memo = {} - - self.Root = {} - self.SConstruct_dir = None - self.max_drift = default_max_drift - - self.Top = None - if path is None: - self.pathTop = os.getcwd() - else: - self.pathTop = path - self.defaultDrive = _my_normcase(_my_splitdrive(self.pathTop)[0]) - - self.Top = self.Dir(self.pathTop) - self.Top.path = '.' - self.Top.tpath = '.' - self._cwd = self.Top - - DirNodeInfo.fs = self - FileNodeInfo.fs = self - - def set_SConstruct_dir(self, dir): - self.SConstruct_dir = dir - - def get_max_drift(self): - return self.max_drift - - def set_max_drift(self, max_drift): - self.max_drift = max_drift - - def getcwd(self): - if hasattr(self, "_cwd"): - return self._cwd - else: - return "" - - def chdir(self, dir, change_os_dir=0): - """Change the current working directory for lookups. - If change_os_dir is true, we will also change the "real" cwd - to match. - """ - curr=self._cwd - try: - if dir is not None: - self._cwd = dir - if change_os_dir: - os.chdir(dir.abspath) - except OSError: - self._cwd = curr - raise - - def get_root(self, drive): - """ - Returns the root directory for the specified drive, creating - it if necessary. - """ - drive = _my_normcase(drive) - try: - return self.Root[drive] - except KeyError: - root = RootDir(drive, self) - self.Root[drive] = root - if not drive: - self.Root[self.defaultDrive] = root - elif drive == self.defaultDrive: - self.Root[''] = root - return root - - def _lookup(self, p, directory, fsclass, create=1): - """ - The generic entry point for Node lookup with user-supplied data. - - This translates arbitrary input into a canonical Node.FS object - of the specified fsclass. The general approach for strings is - to turn it into a fully normalized absolute path and then call - the root directory's lookup_abs() method for the heavy lifting. - - If the path name begins with '#', it is unconditionally - interpreted relative to the top-level directory of this FS. '#' - is treated as a synonym for the top-level SConstruct directory, - much like '~' is treated as a synonym for the user's home - directory in a UNIX shell. So both '#foo' and '#/foo' refer - to the 'foo' subdirectory underneath the top-level SConstruct - directory. - - If the path name is relative, then the path is looked up relative - to the specified directory, or the current directory (self._cwd, - typically the SConscript directory) if the specified directory - is None. - """ - if isinstance(p, Base): - # It's already a Node.FS object. Make sure it's the right - # class and return. - p.must_be_same(fsclass) - return p - # str(p) in case it's something like a proxy object - p = str(p) - - if not os_sep_is_slash: - p = p.replace(OS_SEP, '/') - - if p[0:1] == '#': - # There was an initial '#', so we strip it and override - # whatever directory they may have specified with the - # top-level SConstruct directory. - p = p[1:] - directory = self.Top - - # There might be a drive letter following the - # '#'. Although it is not described in the SCons man page, - # the regression test suite explicitly tests for that - # syntax. It seems to mean the following thing: - # - # Assuming the the SCons top dir is in C:/xxx/yyy, - # '#X:/toto' means X:/xxx/yyy/toto. - # - # i.e. it assumes that the X: drive has a directory - # structure similar to the one found on drive C:. - if do_splitdrive: - drive, p = _my_splitdrive(p) - if drive: - root = self.get_root(drive) - else: - root = directory.root - else: - root = directory.root - - # We can only strip trailing after splitting the drive - # since the drive might the UNC '//' prefix. - p = p.strip('/') - - needs_normpath = needs_normpath_match(p) - - # The path is relative to the top-level SCons directory. - if p in ('', '.'): - p = directory.labspath - else: - p = directory.labspath + '/' + p - else: - if do_splitdrive: - drive, p = _my_splitdrive(p) - if drive and not p: - # This causes a naked drive letter to be treated - # as a synonym for the root directory on that - # drive. - p = '/' - else: - drive = '' - - # We can only strip trailing '/' since the drive might the - # UNC '//' prefix. - if p != '/': - p = p.rstrip('/') - - needs_normpath = needs_normpath_match(p) - - if p[0:1] == '/': - # Absolute path - root = self.get_root(drive) - else: - # This is a relative lookup or to the current directory - # (the path name is not absolute). Add the string to the - # appropriate directory lookup path, after which the whole - # thing gets normalized. - if directory: - if not isinstance(directory, Dir): - directory = self.Dir(directory) - else: - directory = self._cwd - - if p in ('', '.'): - p = directory.labspath - else: - p = directory.labspath + '/' + p - - if drive: - root = self.get_root(drive) - else: - root = directory.root - - if needs_normpath is not None: - # Normalize a pathname. Will return the same result for - # equivalent paths. - # - # We take advantage of the fact that we have an absolute - # path here for sure. In addition, we know that the - # components of lookup path are separated by slashes at - # this point. Because of this, this code is about 2X - # faster than calling os.path.normpath() followed by - # replacing os.sep with '/' again. - ins = p.split('/')[1:] - outs = [] - for d in ins: - if d == '..': - try: - outs.pop() - except IndexError: - pass - elif d not in ('', '.'): - outs.append(d) - p = '/' + '/'.join(outs) - - return root._lookup_abs(p, fsclass, create) - - def Entry(self, name, directory = None, create = 1): - """Look up or create a generic Entry node with the specified name. - If the name is a relative path (begins with ./, ../, or a file - name), then it is looked up relative to the supplied directory - node, or to the top level directory of the FS (supplied at - construction time) if no directory is supplied. - """ - return self._lookup(name, directory, Entry, create) - - def File(self, name, directory = None, create = 1): - """Look up or create a File node with the specified name. If - the name is a relative path (begins with ./, ../, or a file name), - then it is looked up relative to the supplied directory node, - or to the top level directory of the FS (supplied at construction - time) if no directory is supplied. - - This method will raise TypeError if a directory is found at the - specified path. - """ - return self._lookup(name, directory, File, create) - - def Dir(self, name, directory = None, create = True): - """Look up or create a Dir node with the specified name. If - the name is a relative path (begins with ./, ../, or a file name), - then it is looked up relative to the supplied directory node, - or to the top level directory of the FS (supplied at construction - time) if no directory is supplied. - - This method will raise TypeError if a normal file is found at the - specified path. - """ - return self._lookup(name, directory, Dir, create) - - def VariantDir(self, variant_dir, src_dir, duplicate=1): - """Link the supplied variant directory to the source directory - for purposes of building files.""" - - if not isinstance(src_dir, SCons.Node.Node): - src_dir = self.Dir(src_dir) - if not isinstance(variant_dir, SCons.Node.Node): - variant_dir = self.Dir(variant_dir) - if src_dir.is_under(variant_dir): - raise SCons.Errors.UserError("Source directory cannot be under variant directory.") - if variant_dir.srcdir: - if variant_dir.srcdir == src_dir: - return # We already did this. - raise SCons.Errors.UserError("'%s' already has a source directory: '%s'."%(variant_dir, variant_dir.srcdir)) - variant_dir.link(src_dir, duplicate) - - def Repository(self, *dirs): - """Specify Repository directories to search.""" - for d in dirs: - if not isinstance(d, SCons.Node.Node): - d = self.Dir(d) - self.Top.addRepository(d) - - def variant_dir_target_climb(self, orig, dir, tail): - """Create targets in corresponding variant directories - - Climb the directory tree, and look up path names - relative to any linked variant directories we find. - - Even though this loops and walks up the tree, we don't memoize - the return value because this is really only used to process - the command-line targets. - """ - targets = [] - message = None - fmt = "building associated VariantDir targets: %s" - start_dir = dir - while dir: - for bd in dir.variant_dirs: - if start_dir.is_under(bd): - # If already in the build-dir location, don't reflect - return [orig], fmt % str(orig) - p = os.path.join(bd.path, *tail) - targets.append(self.Entry(p)) - tail = [dir.name] + tail - dir = dir.up() - if targets: - message = fmt % ' '.join(map(str, targets)) - return targets, message - - def Glob(self, pathname, ondisk=True, source=True, strings=False, cwd=None): - """ - Globs - - This is mainly a shim layer - """ - if cwd is None: - cwd = self.getcwd() - return cwd.glob(pathname, ondisk, source, strings) - -class DirNodeInfo(SCons.Node.NodeInfoBase): - # This should get reset by the FS initialization. - current_version_id = 1 - - fs = None - - def str_to_node(self, s): - top = self.fs.Top - root = top.root - if do_splitdrive: - drive, s = _my_splitdrive(s) - if drive: - root = self.fs.get_root(drive) - if not os.path.isabs(s): - s = top.labspath + '/' + s - return root._lookup_abs(s, Entry) - -class DirBuildInfo(SCons.Node.BuildInfoBase): - current_version_id = 1 - -glob_magic_check = re.compile('[*?[]') - -def has_glob_magic(s): - return glob_magic_check.search(s) is not None - -class Dir(Base): - """A class for directories in a file system. - """ - - memoizer_counters = [] - - NodeInfo = DirNodeInfo - BuildInfo = DirBuildInfo - - def __init__(self, name, directory, fs): - if __debug__: logInstanceCreation(self, 'Node.FS.Dir') - Base.__init__(self, name, directory, fs) - self._morph() - - def _morph(self): - """Turn a file system Node (either a freshly initialized directory - object or a separate Entry object) into a proper directory object. - - Set up this directory's entries and hook it into the file - system tree. Specify that directories (this Node) don't use - signatures for calculating whether they're current. - """ - - self.repositories = [] - self.srcdir = None - - self.entries = {} - self.entries['.'] = self - self.entries['..'] = self.dir - self.cwd = self - self.searched = 0 - self._sconsign = None - self.variant_dirs = [] - self.root = self.dir.root - - # For directories, we make a difference between the directory - # 'name' and the directory 'dirname'. The 'name' attribute is - # used when we need to print the 'name' of the directory or - # when we it is used as the last part of a path. The 'dirname' - # is used when the directory is not the last element of the - # path. The main reason for making that distinction is that - # for RoorDir's the dirname can not be easily inferred from - # the name. For example, we have to add a '/' after a drive - # letter but not after a UNC path prefix ('//'). - self.dirname = self.name + OS_SEP - - # Don't just reset the executor, replace its action list, - # because it might have some pre-or post-actions that need to - # be preserved. - # - # But don't reset the executor if there is a non-null executor - # attached already. The existing executor might have other - # targets, in which case replacing the action list with a - # Mkdir action is a big mistake. - if not hasattr(self, 'executor'): - self.builder = get_MkdirBuilder() - self.get_executor().set_action_list(self.builder.action) - else: - # Prepend MkdirBuilder action to existing action list - l = self.get_executor().action_list - a = get_MkdirBuilder().action - l.insert(0, a) - self.get_executor().set_action_list(l) - - def diskcheck_match(self): - diskcheck_match(self, self.isfile, - "File %s found where directory expected.") - - def __clearRepositoryCache(self, duplicate=None): - """Called when we change the repository(ies) for a directory. - This clears any cached information that is invalidated by changing - the repository.""" - - for node in self.entries.values(): - if node != self.dir: - if node != self and isinstance(node, Dir): - node.__clearRepositoryCache(duplicate) - else: - node.clear() - try: - del node._srcreps - except AttributeError: - pass - if duplicate is not None: - node.duplicate=duplicate - - def __resetDuplicate(self, node): - if node != self: - node.duplicate = node.get_dir().duplicate - - def Entry(self, name): - """ - Looks up or creates an entry node named 'name' relative to - this directory. - """ - return self.fs.Entry(name, self) - - def Dir(self, name, create=True): - """ - Looks up or creates a directory node named 'name' relative to - this directory. - """ - return self.fs.Dir(name, self, create) - - def File(self, name): - """ - Looks up or creates a file node named 'name' relative to - this directory. - """ - return self.fs.File(name, self) - - def link(self, srcdir, duplicate): - """Set this directory as the variant directory for the - supplied source directory.""" - self.srcdir = srcdir - self.duplicate = duplicate - self.__clearRepositoryCache(duplicate) - srcdir.variant_dirs.append(self) - - def getRepositories(self): - """Returns a list of repositories for this directory. - """ - if self.srcdir and not self.duplicate: - return self.srcdir.get_all_rdirs() + self.repositories - return self.repositories - - memoizer_counters.append(SCons.Memoize.CountValue('get_all_rdirs')) - - def get_all_rdirs(self): - try: - return list(self._memo['get_all_rdirs']) - except KeyError: - pass - - result = [self] - fname = '.' - dir = self - while dir: - for rep in dir.getRepositories(): - result.append(rep.Dir(fname)) - if fname == '.': - fname = dir.name - else: - fname = dir.name + OS_SEP + fname - dir = dir.up() - - self._memo['get_all_rdirs'] = list(result) - - return result - - def addRepository(self, dir): - if dir != self and not dir in self.repositories: - self.repositories.append(dir) - dir.tpath = '.' - self.__clearRepositoryCache() - - def up(self): - return self.dir - - def _rel_path_key(self, other): - return str(other) - - memoizer_counters.append(SCons.Memoize.CountDict('rel_path', _rel_path_key)) - - def rel_path(self, other): - """Return a path to "other" relative to this directory. - """ - - # This complicated and expensive method, which constructs relative - # paths between arbitrary Node.FS objects, is no longer used - # by SCons itself. It was introduced to store dependency paths - # in .sconsign files relative to the target, but that ended up - # being significantly inefficient. - # - # We're continuing to support the method because some SConstruct - # files out there started using it when it was available, and - # we're all about backwards compatibility.. - - try: - memo_dict = self._memo['rel_path'] - except KeyError: - memo_dict = {} - self._memo['rel_path'] = memo_dict - else: - try: - return memo_dict[other] - except KeyError: - pass - - if self is other: - result = '.' - - elif not other in self.path_elements: - try: - other_dir = other.get_dir() - except AttributeError: - result = str(other) - else: - if other_dir is None: - result = other.name - else: - dir_rel_path = self.rel_path(other_dir) - if dir_rel_path == '.': - result = other.name - else: - result = dir_rel_path + OS_SEP + other.name - else: - i = self.path_elements.index(other) + 1 - - path_elems = ['..'] * (len(self.path_elements) - i) \ - + [n.name for n in other.path_elements[i:]] - - result = OS_SEP.join(path_elems) - - memo_dict[other] = result - - return result - - def get_env_scanner(self, env, kw={}): - import SCons.Defaults - return SCons.Defaults.DirEntryScanner - - def get_target_scanner(self): - import SCons.Defaults - return SCons.Defaults.DirEntryScanner - - def get_found_includes(self, env, scanner, path): - """Return this directory's implicit dependencies. - - We don't bother caching the results because the scan typically - shouldn't be requested more than once (as opposed to scanning - .h file contents, which can be requested as many times as the - files is #included by other files). - """ - if not scanner: - return [] - # Clear cached info for this Dir. If we already visited this - # directory on our walk down the tree (because we didn't know at - # that point it was being used as the source for another Node) - # then we may have calculated build signature before realizing - # we had to scan the disk. Now that we have to, though, we need - # to invalidate the old calculated signature so that any node - # dependent on our directory structure gets one that includes - # info about everything on disk. - self.clear() - return scanner(self, env, path) - - # - # Taskmaster interface subsystem - # - - def prepare(self): - pass - - def build(self, **kw): - """A null "builder" for directories.""" - global MkdirBuilder - if self.builder is not MkdirBuilder: - SCons.Node.Node.build(self, **kw) - - # - # - # - - def _create(self): - """Create this directory, silently and without worrying about - whether the builder is the default or not.""" - listDirs = [] - parent = self - while parent: - if parent.exists(): - break - listDirs.append(parent) - p = parent.up() - if p is None: - # Don't use while: - else: for this condition because - # if so, then parent is None and has no .path attribute. - raise SCons.Errors.StopError(parent.path) - parent = p - listDirs.reverse() - for dirnode in listDirs: - try: - # Don't call dirnode.build(), call the base Node method - # directly because we definitely *must* create this - # directory. The dirnode.build() method will suppress - # the build if it's the default builder. - SCons.Node.Node.build(dirnode) - dirnode.get_executor().nullify() - # The build() action may or may not have actually - # created the directory, depending on whether the -n - # option was used or not. Delete the _exists and - # _rexists attributes so they can be reevaluated. - dirnode.clear() - except OSError: - pass - - def multiple_side_effect_has_builder(self): - global MkdirBuilder - return self.builder is not MkdirBuilder and self.has_builder() - - def alter_targets(self): - """Return any corresponding targets in a variant directory. - """ - return self.fs.variant_dir_target_climb(self, self, []) - - def scanner_key(self): - """A directory does not get scanned.""" - return None - - def get_text_contents(self): - """We already emit things in text, so just return the binary - version.""" - return self.get_contents() - - def get_contents(self): - """Return content signatures and names of all our children - separated by new-lines. Ensure that the nodes are sorted.""" - contents = [] - for node in sorted(self.children(), key=lambda t: t.name): - contents.append('%s %s\n' % (node.get_csig(), node.name)) - return ''.join(contents) - - def get_csig(self): - """Compute the content signature for Directory nodes. In - general, this is not needed and the content signature is not - stored in the DirNodeInfo. However, if get_contents on a Dir - node is called which has a child directory, the child - directory should return the hash of its contents.""" - contents = self.get_contents() - return SCons.Util.MD5signature(contents) - - def do_duplicate(self, src): - pass - - changed_since_last_build = SCons.Node.Node.state_has_changed - - def is_up_to_date(self): - """If any child is not up-to-date, then this directory isn't, - either.""" - if self.builder is not MkdirBuilder and not self.exists(): - return 0 - up_to_date = SCons.Node.up_to_date - for kid in self.children(): - if kid.get_state() > up_to_date: - return 0 - return 1 - - def rdir(self): - if not self.exists(): - norm_name = _my_normcase(self.name) - for dir in self.dir.get_all_rdirs(): - try: node = dir.entries[norm_name] - except KeyError: node = dir.dir_on_disk(self.name) - if node and node.exists() and \ - (isinstance(dir, Dir) or isinstance(dir, Entry)): - return node - return self - - def sconsign(self): - """Return the .sconsign file info for this directory, - creating it first if necessary.""" - if not self._sconsign: - import SCons.SConsign - self._sconsign = SCons.SConsign.ForDirectory(self) - return self._sconsign - - def srcnode(self): - """Dir has a special need for srcnode()...if we - have a srcdir attribute set, then that *is* our srcnode.""" - if self.srcdir: - return self.srcdir - return Base.srcnode(self) - - def get_timestamp(self): - """Return the latest timestamp from among our children""" - stamp = 0 - for kid in self.children(): - if kid.get_timestamp() > stamp: - stamp = kid.get_timestamp() - return stamp - - def entry_abspath(self, name): - return self.abspath + OS_SEP + name - - def entry_labspath(self, name): - return self.labspath + '/' + name - - def entry_path(self, name): - return self.path + OS_SEP + name - - def entry_tpath(self, name): - return self.tpath + OS_SEP + name - - def entry_exists_on_disk(self, name): - try: - d = self.on_disk_entries - except AttributeError: - d = {} - try: - entries = os.listdir(self.abspath) - except OSError: - pass - else: - for entry in map(_my_normcase, entries): - d[entry] = True - self.on_disk_entries = d - if sys.platform == 'win32': - name = _my_normcase(name) - result = d.get(name) - if result is None: - # Belt-and-suspenders for Windows: check directly for - # 8.3 file names that don't show up in os.listdir(). - result = os.path.exists(self.abspath + OS_SEP + name) - d[name] = result - return result - else: - return name in d - - memoizer_counters.append(SCons.Memoize.CountValue('srcdir_list')) - - def srcdir_list(self): - try: - return self._memo['srcdir_list'] - except KeyError: - pass - - result = [] - - dirname = '.' - dir = self - while dir: - if dir.srcdir: - result.append(dir.srcdir.Dir(dirname)) - dirname = dir.name + OS_SEP + dirname - dir = dir.up() - - self._memo['srcdir_list'] = result - - return result - - def srcdir_duplicate(self, name): - for dir in self.srcdir_list(): - if self.is_under(dir): - # We shouldn't source from something in the build path; - # variant_dir is probably under src_dir, in which case - # we are reflecting. - break - if dir.entry_exists_on_disk(name): - srcnode = dir.Entry(name).disambiguate() - if self.duplicate: - node = self.Entry(name).disambiguate() - node.do_duplicate(srcnode) - return node - else: - return srcnode - return None - - def _srcdir_find_file_key(self, filename): - return filename - - memoizer_counters.append(SCons.Memoize.CountDict('srcdir_find_file', _srcdir_find_file_key)) - - def srcdir_find_file(self, filename): - try: - memo_dict = self._memo['srcdir_find_file'] - except KeyError: - memo_dict = {} - self._memo['srcdir_find_file'] = memo_dict - else: - try: - return memo_dict[filename] - except KeyError: - pass - - def func(node): - if (isinstance(node, File) or isinstance(node, Entry)) and \ - (node.is_derived() or node.exists()): - return node - return None - - norm_name = _my_normcase(filename) - - for rdir in self.get_all_rdirs(): - try: node = rdir.entries[norm_name] - except KeyError: node = rdir.file_on_disk(filename) - else: node = func(node) - if node: - result = (node, self) - memo_dict[filename] = result - return result - - for srcdir in self.srcdir_list(): - for rdir in srcdir.get_all_rdirs(): - try: node = rdir.entries[norm_name] - except KeyError: node = rdir.file_on_disk(filename) - else: node = func(node) - if node: - result = (File(filename, self, self.fs), srcdir) - memo_dict[filename] = result - return result - - result = (None, None) - memo_dict[filename] = result - return result - - def dir_on_disk(self, name): - if self.entry_exists_on_disk(name): - try: return self.Dir(name) - except TypeError: pass - node = self.srcdir_duplicate(name) - if isinstance(node, File): - return None - return node - - def file_on_disk(self, name): - if self.entry_exists_on_disk(name) or \ - diskcheck_rcs(self, name) or \ - diskcheck_sccs(self, name): - try: return self.File(name) - except TypeError: pass - node = self.srcdir_duplicate(name) - if isinstance(node, Dir): - return None - return node - - def walk(self, func, arg): - """ - Walk this directory tree by calling the specified function - for each directory in the tree. - - This behaves like the os.path.walk() function, but for in-memory - Node.FS.Dir objects. The function takes the same arguments as - the functions passed to os.path.walk(): - - func(arg, dirname, fnames) - - Except that "dirname" will actually be the directory *Node*, - not the string. The '.' and '..' entries are excluded from - fnames. The fnames list may be modified in-place to filter the - subdirectories visited or otherwise impose a specific order. - The "arg" argument is always passed to func() and may be used - in any way (or ignored, passing None is common). - """ - entries = self.entries - names = list(entries.keys()) - names.remove('.') - names.remove('..') - func(arg, self, names) - for dirname in [n for n in names if isinstance(entries[n], Dir)]: - entries[dirname].walk(func, arg) - - def glob(self, pathname, ondisk=True, source=False, strings=False): - """ - Returns a list of Nodes (or strings) matching a specified - pathname pattern. - - Pathname patterns follow UNIX shell semantics: * matches - any-length strings of any characters, ? matches any character, - and [] can enclose lists or ranges of characters. Matches do - not span directory separators. - - The matches take into account Repositories, returning local - Nodes if a corresponding entry exists in a Repository (either - an in-memory Node or something on disk). - - By defafult, the glob() function matches entries that exist - on-disk, in addition to in-memory Nodes. Setting the "ondisk" - argument to False (or some other non-true value) causes the glob() - function to only match in-memory Nodes. The default behavior is - to return both the on-disk and in-memory Nodes. - - The "source" argument, when true, specifies that corresponding - source Nodes must be returned if you're globbing in a build - directory (initialized with VariantDir()). The default behavior - is to return Nodes local to the VariantDir(). - - The "strings" argument, when true, returns the matches as strings, - not Nodes. The strings are path names relative to this directory. - - The underlying algorithm is adapted from the glob.glob() function - in the Python library (but heavily modified), and uses fnmatch() - under the covers. - """ - dirname, basename = os.path.split(pathname) - if not dirname: - return sorted(self._glob1(basename, ondisk, source, strings), - key=lambda t: str(t)) - if has_glob_magic(dirname): - list = self.glob(dirname, ondisk, source, strings=False) - else: - list = [self.Dir(dirname, create=True)] - result = [] - for dir in list: - r = dir._glob1(basename, ondisk, source, strings) - if strings: - r = [os.path.join(str(dir), x) for x in r] - result.extend(r) - return sorted(result, key=lambda a: str(a)) - - def _glob1(self, pattern, ondisk=True, source=False, strings=False): - """ - Globs for and returns a list of entry names matching a single - pattern in this directory. - - This searches any repositories and source directories for - corresponding entries and returns a Node (or string) relative - to the current directory if an entry is found anywhere. - - TODO: handle pattern with no wildcard - """ - search_dir_list = self.get_all_rdirs() - for srcdir in self.srcdir_list(): - search_dir_list.extend(srcdir.get_all_rdirs()) - - selfEntry = self.Entry - names = [] - for dir in search_dir_list: - # We use the .name attribute from the Node because the keys of - # the dir.entries dictionary are normalized (that is, all upper - # case) on case-insensitive systems like Windows. - node_names = [ v.name for k, v in dir.entries.items() - if k not in ('.', '..') ] - names.extend(node_names) - if not strings: - # Make sure the working directory (self) actually has - # entries for all Nodes in repositories or variant dirs. - for name in node_names: selfEntry(name) - if ondisk: - try: - disk_names = os.listdir(dir.abspath) - except os.error: - continue - names.extend(disk_names) - if not strings: - # We're going to return corresponding Nodes in - # the local directory, so we need to make sure - # those Nodes exist. We only want to create - # Nodes for the entries that will match the - # specified pattern, though, which means we - # need to filter the list here, even though - # the overall list will also be filtered later, - # after we exit this loop. - if pattern[0] != '.': - #disk_names = [ d for d in disk_names if d[0] != '.' ] - disk_names = [x for x in disk_names if x[0] != '.'] - disk_names = fnmatch.filter(disk_names, pattern) - dirEntry = dir.Entry - for name in disk_names: - # Add './' before disk filename so that '#' at - # beginning of filename isn't interpreted. - name = './' + name - node = dirEntry(name).disambiguate() - n = selfEntry(name) - if n.__class__ != node.__class__: - n.__class__ = node.__class__ - n._morph() - - names = set(names) - if pattern[0] != '.': - #names = [ n for n in names if n[0] != '.' ] - names = [x for x in names if x[0] != '.'] - names = fnmatch.filter(names, pattern) - - if strings: - return names - - #return [ self.entries[_my_normcase(n)] for n in names ] - return [self.entries[_my_normcase(n)] for n in names] - -class RootDir(Dir): - """A class for the root directory of a file system. - - This is the same as a Dir class, except that the path separator - ('/' or '\\') is actually part of the name, so we don't need to - add a separator when creating the path names of entries within - this directory. - """ - def __init__(self, drive, fs): - if __debug__: logInstanceCreation(self, 'Node.FS.RootDir') - # We're going to be our own parent directory (".." entry and .dir - # attribute) so we have to set up some values so Base.__init__() - # won't gag won't it calls some of our methods. - self.abspath = '' - self.labspath = '' - self.path = '' - self.tpath = '' - self.path_elements = [] - self.duplicate = 0 - self.root = self - - # Handle all the types of drives: - if drive == '': - # No drive, regular UNIX root or Windows default drive. - name = OS_SEP - dirname = OS_SEP - elif drive == '//': - # UNC path - name = UNC_PREFIX - dirname = UNC_PREFIX - else: - # Windows drive letter - name = drive - dirname = drive + OS_SEP - - Base.__init__(self, name, self, fs) - - # Now set our paths to what we really want them to be. The - # name should already contain any necessary separators, such - # as the initial drive letter (the name) plus the directory - # separator, except for the "lookup abspath," which does not - # have the drive letter. - self.abspath = dirname - self.labspath = '' - self.path = dirname - self.tpath = dirname - self._morph() - - # Must be reset after Dir._morph() is invoked... - self.dirname = dirname - - self._lookupDict = {} - - self._lookupDict[''] = self - self._lookupDict['/'] = self - - # The // entry is necessary because os.path.normpath() - # preserves double slashes at the beginning of a path on Posix - # platforms. - if not has_unc: - self._lookupDict['//'] = self - - def must_be_same(self, klass): - if klass is Dir: - return - Base.must_be_same(self, klass) - - def _lookup_abs(self, p, klass, create=1): - """ - Fast (?) lookup of a *normalized* absolute path. - - This method is intended for use by internal lookups with - already-normalized path data. For general-purpose lookups, - use the FS.Entry(), FS.Dir() or FS.File() methods. - - The caller is responsible for making sure we're passed a - normalized absolute path; we merely let Python's dictionary look - up and return the One True Node.FS object for the path. - - If a Node for the specified "p" doesn't already exist, and - "create" is specified, the Node may be created after recursive - invocation to find or create the parent directory or directories. - """ - k = _my_normcase(p) - try: - result = self._lookupDict[k] - except KeyError: - if not create: - msg = "No such file or directory: '%s' in '%s' (and create is False)" % (p, str(self)) - raise SCons.Errors.UserError(msg) - # There is no Node for this path name, and we're allowed - # to create it. - # (note: would like to use p.rsplit('/',1) here but - # that's not in python 2.3) - # e.g.: dir_name, file_name = p.rsplit('/',1) - last_slash = p.rindex('/') - if (last_slash >= 0): - dir_name = p[:last_slash] - file_name = p[last_slash+1:] - else: - dir_name = p # shouldn't happen, just in case - file_name = '' - - dir_node = self._lookup_abs(dir_name, Dir) - result = klass(file_name, dir_node, self.fs) - - # Double-check on disk (as configured) that the Node we - # created matches whatever is out there in the real world. - result.diskcheck_match() - - self._lookupDict[k] = result - dir_node.entries[_my_normcase(file_name)] = result - dir_node.implicit = None - else: - # There is already a Node for this path name. Allow it to - # complain if we were looking for an inappropriate type. - result.must_be_same(klass) - return result - - def __str__(self): - return self.abspath - - def entry_abspath(self, name): - return self.abspath + name - - def entry_labspath(self, name): - return '/' + name - - def entry_path(self, name): - return self.path + name - - def entry_tpath(self, name): - return self.tpath + name - - def is_under(self, dir): - if self is dir: - return 1 - else: - return 0 - - def up(self): - return None - - def get_dir(self): - return None - - def src_builder(self): - return _null - -class FileNodeInfo(SCons.Node.NodeInfoBase): - current_version_id = 1 - - field_list = ['csig', 'timestamp', 'size'] - - # This should get reset by the FS initialization. - fs = None - - def str_to_node(self, s): - top = self.fs.Top - root = top.root - if do_splitdrive: - drive, s = _my_splitdrive(s) - if drive: - root = self.fs.get_root(drive) - if not os.path.isabs(s): - s = top.labspath + '/' + s - return root._lookup_abs(s, Entry) - -class FileBuildInfo(SCons.Node.BuildInfoBase): - current_version_id = 1 - - def convert_to_sconsign(self): - """ - Converts this FileBuildInfo object for writing to a .sconsign file - - This replaces each Node in our various dependency lists with its - usual string representation: relative to the top-level SConstruct - directory, or an absolute path if it's outside. - """ - if os_sep_is_slash: - node_to_str = str - else: - def node_to_str(n): - try: - s = n.path - except AttributeError: - s = str(n) - else: - s = s.replace(OS_SEP, '/') - return s - for attr in ['bsources', 'bdepends', 'bimplicit']: - try: - val = getattr(self, attr) - except AttributeError: - pass - else: - setattr(self, attr, list(map(node_to_str, val))) - def convert_from_sconsign(self, dir, name): - """ - Converts a newly-read FileBuildInfo object for in-SCons use - - For normal up-to-date checking, we don't have any conversion to - perform--but we're leaving this method here to make that clear. - """ - pass - def prepare_dependencies(self): - """ - Prepares a FileBuildInfo object for explaining what changed - - The bsources, bdepends and bimplicit lists have all been - stored on disk as paths relative to the top-level SConstruct - directory. Convert the strings to actual Nodes (for use by the - --debug=explain code and --implicit-cache). - """ - attrs = [ - ('bsources', 'bsourcesigs'), - ('bdepends', 'bdependsigs'), - ('bimplicit', 'bimplicitsigs'), - ] - for (nattr, sattr) in attrs: - try: - strings = getattr(self, nattr) - nodeinfos = getattr(self, sattr) - except AttributeError: - continue - nodes = [] - for s, ni in zip(strings, nodeinfos): - if not isinstance(s, SCons.Node.Node): - s = ni.str_to_node(s) - nodes.append(s) - setattr(self, nattr, nodes) - def format(self, names=0): - result = [] - bkids = self.bsources + self.bdepends + self.bimplicit - bkidsigs = self.bsourcesigs + self.bdependsigs + self.bimplicitsigs - for bkid, bkidsig in zip(bkids, bkidsigs): - result.append(str(bkid) + ': ' + - ' '.join(bkidsig.format(names=names))) - result.append('%s [%s]' % (self.bactsig, self.bact)) - return '\n'.join(result) - -class File(Base): - """A class for files in a file system. - """ - - memoizer_counters = [] - - NodeInfo = FileNodeInfo - BuildInfo = FileBuildInfo - - md5_chunksize = 64 - - def diskcheck_match(self): - diskcheck_match(self, self.isdir, - "Directory %s found where file expected.") - - def __init__(self, name, directory, fs): - if __debug__: logInstanceCreation(self, 'Node.FS.File') - Base.__init__(self, name, directory, fs) - self._morph() - - def Entry(self, name): - """Create an entry node named 'name' relative to - the directory of this file.""" - return self.dir.Entry(name) - - def Dir(self, name, create=True): - """Create a directory node named 'name' relative to - the directory of this file.""" - return self.dir.Dir(name, create=create) - - def Dirs(self, pathlist): - """Create a list of directories relative to the SConscript - directory of this file.""" - return [self.Dir(p) for p in pathlist] - - def File(self, name): - """Create a file node named 'name' relative to - the directory of this file.""" - return self.dir.File(name) - - #def generate_build_dict(self): - # """Return an appropriate dictionary of values for building - # this File.""" - # return {'Dir' : self.Dir, - # 'File' : self.File, - # 'RDirs' : self.RDirs} - - def _morph(self): - """Turn a file system node into a File object.""" - self.scanner_paths = {} - if not hasattr(self, '_local'): - self._local = 0 - - # If there was already a Builder set on this entry, then - # we need to make sure we call the target-decider function, - # not the source-decider. Reaching in and doing this by hand - # is a little bogus. We'd prefer to handle this by adding - # an Entry.builder_set() method that disambiguates like the - # other methods, but that starts running into problems with the - # fragile way we initialize Dir Nodes with their Mkdir builders, - # yet still allow them to be overridden by the user. Since it's - # not clear right now how to fix that, stick with what works - # until it becomes clear... - if self.has_builder(): - self.changed_since_last_build = self.decide_target - - def scanner_key(self): - return self.get_suffix() - - def get_contents(self): - if not self.rexists(): - return '' - fname = self.rfile().abspath - try: - contents = open(fname, "rb").read() - except EnvironmentError, e: - if not e.filename: - e.filename = fname - raise - return contents - - # This attempts to figure out what the encoding of the text is - # based upon the BOM bytes, and then decodes the contents so that - # it's a valid python string. - def get_text_contents(self): - contents = self.get_contents() - # The behavior of various decode() methods and functions - # w.r.t. the initial BOM bytes is different for different - # encodings and/or Python versions. ('utf-8' does not strip - # them, but has a 'utf-8-sig' which does; 'utf-16' seems to - # strip them; etc.) Just sidestep all the complication by - # explicitly stripping the BOM before we decode(). - if contents.startswith(codecs.BOM_UTF8): - return contents[len(codecs.BOM_UTF8):].decode('utf-8') - if contents.startswith(codecs.BOM_UTF16_LE): - return contents[len(codecs.BOM_UTF16_LE):].decode('utf-16-le') - if contents.startswith(codecs.BOM_UTF16_BE): - return contents[len(codecs.BOM_UTF16_BE):].decode('utf-16-be') - return contents - - def get_content_hash(self): - """ - Compute and return the MD5 hash for this file. - """ - if not self.rexists(): - return SCons.Util.MD5signature('') - fname = self.rfile().abspath - try: - cs = SCons.Util.MD5filesignature(fname, - chunksize=SCons.Node.FS.File.md5_chunksize*1024) - except EnvironmentError, e: - if not e.filename: - e.filename = fname - raise - return cs - - - memoizer_counters.append(SCons.Memoize.CountValue('get_size')) - - def get_size(self): - try: - return self._memo['get_size'] - except KeyError: - pass - - if self.rexists(): - size = self.rfile().getsize() - else: - size = 0 - - self._memo['get_size'] = size - - return size - - memoizer_counters.append(SCons.Memoize.CountValue('get_timestamp')) - - def get_timestamp(self): - try: - return self._memo['get_timestamp'] - except KeyError: - pass - - if self.rexists(): - timestamp = self.rfile().getmtime() - else: - timestamp = 0 - - self._memo['get_timestamp'] = timestamp - - return timestamp - - def store_info(self): - # Merge our build information into the already-stored entry. - # This accomodates "chained builds" where a file that's a target - # in one build (SConstruct file) is a source in a different build. - # See test/chained-build.py for the use case. - if do_store_info: - self.dir.sconsign().store_info(self.name, self) - - convert_copy_attrs = [ - 'bsources', - 'bimplicit', - 'bdepends', - 'bact', - 'bactsig', - 'ninfo', - ] - - - convert_sig_attrs = [ - 'bsourcesigs', - 'bimplicitsigs', - 'bdependsigs', - ] - - def convert_old_entry(self, old_entry): - # Convert a .sconsign entry from before the Big Signature - # Refactoring, doing what we can to convert its information - # to the new .sconsign entry format. - # - # The old format looked essentially like this: - # - # BuildInfo - # .ninfo (NodeInfo) - # .bsig - # .csig - # .timestamp - # .size - # .bsources - # .bsourcesigs ("signature" list) - # .bdepends - # .bdependsigs ("signature" list) - # .bimplicit - # .bimplicitsigs ("signature" list) - # .bact - # .bactsig - # - # The new format looks like this: - # - # .ninfo (NodeInfo) - # .bsig - # .csig - # .timestamp - # .size - # .binfo (BuildInfo) - # .bsources - # .bsourcesigs (NodeInfo list) - # .bsig - # .csig - # .timestamp - # .size - # .bdepends - # .bdependsigs (NodeInfo list) - # .bsig - # .csig - # .timestamp - # .size - # .bimplicit - # .bimplicitsigs (NodeInfo list) - # .bsig - # .csig - # .timestamp - # .size - # .bact - # .bactsig - # - # The basic idea of the new structure is that a NodeInfo always - # holds all available information about the state of a given Node - # at a certain point in time. The various .b*sigs lists can just - # be a list of pointers to the .ninfo attributes of the different - # dependent nodes, without any copying of information until it's - # time to pickle it for writing out to a .sconsign file. - # - # The complicating issue is that the *old* format only stored one - # "signature" per dependency, based on however the *last* build - # was configured. We don't know from just looking at it whether - # it was a build signature, a content signature, or a timestamp - # "signature". Since we no longer use build signatures, the - # best we can do is look at the length and if it's thirty two, - # assume that it was (or might have been) a content signature. - # If it was actually a build signature, then it will cause a - # rebuild anyway when it doesn't match the new content signature, - # but that's probably the best we can do. - import SCons.SConsign - new_entry = SCons.SConsign.SConsignEntry() - new_entry.binfo = self.new_binfo() - binfo = new_entry.binfo - for attr in self.convert_copy_attrs: - try: - value = getattr(old_entry, attr) - except AttributeError: - continue - setattr(binfo, attr, value) - delattr(old_entry, attr) - for attr in self.convert_sig_attrs: - try: - sig_list = getattr(old_entry, attr) - except AttributeError: - continue - value = [] - for sig in sig_list: - ninfo = self.new_ninfo() - if len(sig) == 32: - ninfo.csig = sig - else: - ninfo.timestamp = sig - value.append(ninfo) - setattr(binfo, attr, value) - delattr(old_entry, attr) - return new_entry - - memoizer_counters.append(SCons.Memoize.CountValue('get_stored_info')) - - def get_stored_info(self): - try: - return self._memo['get_stored_info'] - except KeyError: - pass - - try: - sconsign_entry = self.dir.sconsign().get_entry(self.name) - except (KeyError, EnvironmentError): - import SCons.SConsign - sconsign_entry = SCons.SConsign.SConsignEntry() - sconsign_entry.binfo = self.new_binfo() - sconsign_entry.ninfo = self.new_ninfo() - else: - if isinstance(sconsign_entry, FileBuildInfo): - # This is a .sconsign file from before the Big Signature - # Refactoring; convert it as best we can. - sconsign_entry = self.convert_old_entry(sconsign_entry) - try: - delattr(sconsign_entry.ninfo, 'bsig') - except AttributeError: - pass - - self._memo['get_stored_info'] = sconsign_entry - - return sconsign_entry - - def get_stored_implicit(self): - binfo = self.get_stored_info().binfo - binfo.prepare_dependencies() - try: return binfo.bimplicit - except AttributeError: return None - - def rel_path(self, other): - return self.dir.rel_path(other) - - def _get_found_includes_key(self, env, scanner, path): - return (id(env), id(scanner), path) - - memoizer_counters.append(SCons.Memoize.CountDict('get_found_includes', _get_found_includes_key)) - - def get_found_includes(self, env, scanner, path): - """Return the included implicit dependencies in this file. - Cache results so we only scan the file once per path - regardless of how many times this information is requested. - """ - memo_key = (id(env), id(scanner), path) - try: - memo_dict = self._memo['get_found_includes'] - except KeyError: - memo_dict = {} - self._memo['get_found_includes'] = memo_dict - else: - try: - return memo_dict[memo_key] - except KeyError: - pass - - if scanner: - # result = [n.disambiguate() for n in scanner(self, env, path)] - result = scanner(self, env, path) - result = [N.disambiguate() for N in result] - else: - result = [] - - memo_dict[memo_key] = result - - return result - - def _createDir(self): - # ensure that the directories for this node are - # created. - self.dir._create() - - def push_to_cache(self): - """Try to push the node into a cache - """ - # This should get called before the Nodes' .built() method is - # called, which would clear the build signature if the file has - # a source scanner. - # - # We have to clear the local memoized values *before* we push - # the node to cache so that the memoization of the self.exists() - # return value doesn't interfere. - if self.nocache: - return - self.clear_memoized_values() - if self.exists(): - self.get_build_env().get_CacheDir().push(self) - - def retrieve_from_cache(self): - """Try to retrieve the node's content from a cache - - This method is called from multiple threads in a parallel build, - so only do thread safe stuff here. Do thread unsafe stuff in - built(). - - Returns true if the node was successfully retrieved. - """ - if self.nocache: - return None - if not self.is_derived(): - return None - return self.get_build_env().get_CacheDir().retrieve(self) - - def visited(self): - if self.exists(): - self.get_build_env().get_CacheDir().push_if_forced(self) - - ninfo = self.get_ninfo() - - csig = self.get_max_drift_csig() - if csig: - ninfo.csig = csig - - ninfo.timestamp = self.get_timestamp() - ninfo.size = self.get_size() - - if not self.has_builder(): - # This is a source file, but it might have been a target file - # in another build that included more of the DAG. Copy - # any build information that's stored in the .sconsign file - # into our binfo object so it doesn't get lost. - old = self.get_stored_info() - self.get_binfo().__dict__.update(old.binfo.__dict__) - - self.store_info() - - def find_src_builder(self): - if self.rexists(): - return None - scb = self.dir.src_builder() - if scb is _null: - if diskcheck_sccs(self.dir, self.name): - scb = get_DefaultSCCSBuilder() - elif diskcheck_rcs(self.dir, self.name): - scb = get_DefaultRCSBuilder() - else: - scb = None - if scb is not None: - try: - b = self.builder - except AttributeError: - b = None - if b is None: - self.builder_set(scb) - return scb - - def has_src_builder(self): - """Return whether this Node has a source builder or not. - - If this Node doesn't have an explicit source code builder, this - is where we figure out, on the fly, if there's a transparent - source code builder for it. - - Note that if we found a source builder, we also set the - self.builder attribute, so that all of the methods that actually - *build* this file don't have to do anything different. - """ - try: - scb = self.sbuilder - except AttributeError: - scb = self.sbuilder = self.find_src_builder() - return scb is not None - - def alter_targets(self): - """Return any corresponding targets in a variant directory. - """ - if self.is_derived(): - return [], None - return self.fs.variant_dir_target_climb(self, self.dir, [self.name]) - - def _rmv_existing(self): - self.clear_memoized_values() - if print_duplicate: - print "dup: removing existing target %s"%self - e = Unlink(self, [], None) - if isinstance(e, SCons.Errors.BuildError): - raise e - - # - # Taskmaster interface subsystem - # - - def make_ready(self): - self.has_src_builder() - self.get_binfo() - - def prepare(self): - """Prepare for this file to be created.""" - SCons.Node.Node.prepare(self) - - if self.get_state() != SCons.Node.up_to_date: - if self.exists(): - if self.is_derived() and not self.precious: - self._rmv_existing() - else: - try: - self._createDir() - except SCons.Errors.StopError, drive: - desc = "No drive `%s' for target `%s'." % (drive, self) - raise SCons.Errors.StopError(desc) - - # - # - # - - def remove(self): - """Remove this file.""" - if self.exists() or self.islink(): - self.fs.unlink(self.path) - return 1 - return None - - def do_duplicate(self, src): - self._createDir() - if print_duplicate: - print "dup: relinking variant '%s' from '%s'"%(self, src) - Unlink(self, None, None) - e = Link(self, src, None) - if isinstance(e, SCons.Errors.BuildError): - desc = "Cannot duplicate `%s' in `%s': %s." % (src.path, self.dir.path, e.errstr) - raise SCons.Errors.StopError(desc) - self.linked = 1 - # The Link() action may or may not have actually - # created the file, depending on whether the -n - # option was used or not. Delete the _exists and - # _rexists attributes so they can be reevaluated. - self.clear() - - memoizer_counters.append(SCons.Memoize.CountValue('exists')) - - def exists(self): - try: - return self._memo['exists'] - except KeyError: - pass - # Duplicate from source path if we are set up to do this. - if self.duplicate and not self.is_derived() and not self.linked: - src = self.srcnode() - if src is not self: - # At this point, src is meant to be copied in a variant directory. - src = src.rfile() - if src.abspath != self.abspath: - if src.exists(): - self.do_duplicate(src) - # Can't return 1 here because the duplication might - # not actually occur if the -n option is being used. - else: - # The source file does not exist. Make sure no old - # copy remains in the variant directory. - if print_duplicate: - print "dup: no src for %s, unlinking old variant copy"%self - if Base.exists(self) or self.islink(): - self.fs.unlink(self.path) - # Return None explicitly because the Base.exists() call - # above will have cached its value if the file existed. - self._memo['exists'] = None - return None - result = Base.exists(self) - self._memo['exists'] = result - return result - - # - # SIGNATURE SUBSYSTEM - # - - def get_max_drift_csig(self): - """ - Returns the content signature currently stored for this node - if it's been unmodified longer than the max_drift value, or the - max_drift value is 0. Returns None otherwise. - """ - old = self.get_stored_info() - mtime = self.get_timestamp() - - max_drift = self.fs.max_drift - if max_drift > 0: - if (time.time() - mtime) > max_drift: - try: - n = old.ninfo - if n.timestamp and n.csig and n.timestamp == mtime: - return n.csig - except AttributeError: - pass - elif max_drift == 0: - try: - return old.ninfo.csig - except AttributeError: - pass - - return None - - def get_csig(self): - """ - Generate a node's content signature, the digested signature - of its content. - - node - the node - cache - alternate node to use for the signature cache - returns - the content signature - """ - ninfo = self.get_ninfo() - try: - return ninfo.csig - except AttributeError: - pass - - csig = self.get_max_drift_csig() - if csig is None: - - try: - if self.get_size() < SCons.Node.FS.File.md5_chunksize: - contents = self.get_contents() - else: - csig = self.get_content_hash() - except IOError: - # This can happen if there's actually a directory on-disk, - # which can be the case if they've disabled disk checks, - # or if an action with a File target actually happens to - # create a same-named directory by mistake. - csig = '' - else: - if not csig: - csig = SCons.Util.MD5signature(contents) - - ninfo.csig = csig - - return csig - - # - # DECISION SUBSYSTEM - # - - def builder_set(self, builder): - SCons.Node.Node.builder_set(self, builder) - self.changed_since_last_build = self.decide_target - - def changed_content(self, target, prev_ni): - cur_csig = self.get_csig() - try: - return cur_csig != prev_ni.csig - except AttributeError: - return 1 - - def changed_state(self, target, prev_ni): - return self.state != SCons.Node.up_to_date - - def changed_timestamp_then_content(self, target, prev_ni): - if not self.changed_timestamp_match(target, prev_ni): - try: - self.get_ninfo().csig = prev_ni.csig - except AttributeError: - pass - return False - return self.changed_content(target, prev_ni) - - def changed_timestamp_newer(self, target, prev_ni): - try: - return self.get_timestamp() > target.get_timestamp() - except AttributeError: - return 1 - - def changed_timestamp_match(self, target, prev_ni): - try: - return self.get_timestamp() != prev_ni.timestamp - except AttributeError: - return 1 - - def decide_source(self, target, prev_ni): - return target.get_build_env().decide_source(self, target, prev_ni) - - def decide_target(self, target, prev_ni): - return target.get_build_env().decide_target(self, target, prev_ni) - - # Initialize this Node's decider function to decide_source() because - # every file is a source file until it has a Builder attached... - changed_since_last_build = decide_source - - def is_up_to_date(self): - T = 0 - if T: Trace('is_up_to_date(%s):' % self) - if not self.exists(): - if T: Trace(' not self.exists():') - # The file doesn't exist locally... - r = self.rfile() - if r != self: - # ...but there is one in a Repository... - if not self.changed(r): - if T: Trace(' changed(%s):' % r) - # ...and it's even up-to-date... - if self._local: - # ...and they'd like a local copy. - e = LocalCopy(self, r, None) - if isinstance(e, SCons.Errors.BuildError): - raise - self.store_info() - if T: Trace(' 1\n') - return 1 - self.changed() - if T: Trace(' None\n') - return None - else: - r = self.changed() - if T: Trace(' self.exists(): %s\n' % r) - return not r - - memoizer_counters.append(SCons.Memoize.CountValue('rfile')) - - def rfile(self): - try: - return self._memo['rfile'] - except KeyError: - pass - result = self - if not self.exists(): - norm_name = _my_normcase(self.name) - for dir in self.dir.get_all_rdirs(): - try: node = dir.entries[norm_name] - except KeyError: node = dir.file_on_disk(self.name) - if node and node.exists() and \ - (isinstance(node, File) or isinstance(node, Entry) \ - or not node.is_derived()): - result = node - # Copy over our local attributes to the repository - # Node so we identify shared object files in the - # repository and don't assume they're static. - # - # This isn't perfect; the attribute would ideally - # be attached to the object in the repository in - # case it was built statically in the repository - # and we changed it to shared locally, but that's - # rarely the case and would only occur if you - # intentionally used the same suffix for both - # shared and static objects anyway. So this - # should work well in practice. - result.attributes = self.attributes - break - self._memo['rfile'] = result - return result - - def rstr(self): - return str(self.rfile()) - - def get_cachedir_csig(self): - """ - Fetch a Node's content signature for purposes of computing - another Node's cachesig. - - This is a wrapper around the normal get_csig() method that handles - the somewhat obscure case of using CacheDir with the -n option. - Any files that don't exist would normally be "built" by fetching - them from the cache, but the normal get_csig() method will try - to open up the local file, which doesn't exist because the -n - option meant we didn't actually pull the file from cachedir. - But since the file *does* actually exist in the cachedir, we - can use its contents for the csig. - """ - try: - return self.cachedir_csig - except AttributeError: - pass - - cachedir, cachefile = self.get_build_env().get_CacheDir().cachepath(self) - if not self.exists() and cachefile and os.path.exists(cachefile): - self.cachedir_csig = SCons.Util.MD5filesignature(cachefile, \ - SCons.Node.FS.File.md5_chunksize * 1024) - else: - self.cachedir_csig = self.get_csig() - return self.cachedir_csig - - def get_cachedir_bsig(self): - try: - return self.cachesig - except AttributeError: - pass - - # Add the path to the cache signature, because multiple - # targets built by the same action will all have the same - # build signature, and we have to differentiate them somehow. - children = self.children() - executor = self.get_executor() - # sigs = [n.get_cachedir_csig() for n in children] - sigs = [n.get_cachedir_csig() for n in children] - sigs.append(SCons.Util.MD5signature(executor.get_contents())) - sigs.append(self.path) - result = self.cachesig = SCons.Util.MD5collect(sigs) - return result - - -default_fs = None - -def get_default_fs(): - global default_fs - if not default_fs: - default_fs = FS() - return default_fs - -class FileFinder(object): - """ - """ - if SCons.Memoize.use_memoizer: - __metaclass__ = SCons.Memoize.Memoized_Metaclass - - memoizer_counters = [] - - def __init__(self): - self._memo = {} - - def filedir_lookup(self, p, fd=None): - """ - A helper method for find_file() that looks up a directory for - a file we're trying to find. This only creates the Dir Node if - it exists on-disk, since if the directory doesn't exist we know - we won't find any files in it... :-) - - It would be more compact to just use this as a nested function - with a default keyword argument (see the commented-out version - below), but that doesn't work unless you have nested scopes, - so we define it here just so this work under Python 1.5.2. - """ - if fd is None: - fd = self.default_filedir - dir, name = os.path.split(fd) - drive, d = _my_splitdrive(dir) - if not name and d[:1] in ('/', OS_SEP): - #return p.fs.get_root(drive).dir_on_disk(name) - return p.fs.get_root(drive) - if dir: - p = self.filedir_lookup(p, dir) - if not p: - return None - norm_name = _my_normcase(name) - try: - node = p.entries[norm_name] - except KeyError: - return p.dir_on_disk(name) - if isinstance(node, Dir): - return node - if isinstance(node, Entry): - node.must_be_same(Dir) - return node - return None - - def _find_file_key(self, filename, paths, verbose=None): - return (filename, paths) - - memoizer_counters.append(SCons.Memoize.CountDict('find_file', _find_file_key)) - - def find_file(self, filename, paths, verbose=None): - """ - find_file(str, [Dir()]) -> [nodes] - - filename - a filename to find - paths - a list of directory path *nodes* to search in. Can be - represented as a list, a tuple, or a callable that is - called with no arguments and returns the list or tuple. - - returns - the node created from the found file. - - Find a node corresponding to either a derived file or a file - that exists already. - - Only the first file found is returned, and none is returned - if no file is found. - """ - memo_key = self._find_file_key(filename, paths) - try: - memo_dict = self._memo['find_file'] - except KeyError: - memo_dict = {} - self._memo['find_file'] = memo_dict - else: - try: - return memo_dict[memo_key] - except KeyError: - pass - - if verbose and not callable(verbose): - if not SCons.Util.is_String(verbose): - verbose = "find_file" - _verbose = u' %s: ' % verbose - verbose = lambda s: sys.stdout.write(_verbose + s) - - filedir, filename = os.path.split(filename) - if filedir: - # More compact code that we can't use until we drop - # support for Python 1.5.2: - # - #def filedir_lookup(p, fd=filedir): - # """ - # A helper function that looks up a directory for a file - # we're trying to find. This only creates the Dir Node - # if it exists on-disk, since if the directory doesn't - # exist we know we won't find any files in it... :-) - # """ - # dir, name = os.path.split(fd) - # if dir: - # p = filedir_lookup(p, dir) - # if not p: - # return None - # norm_name = _my_normcase(name) - # try: - # node = p.entries[norm_name] - # except KeyError: - # return p.dir_on_disk(name) - # if isinstance(node, Dir): - # return node - # if isinstance(node, Entry): - # node.must_be_same(Dir) - # return node - # if isinstance(node, Dir) or isinstance(node, Entry): - # return node - # return None - #paths = [_f for _f in map(filedir_lookup, paths) if _f] - - self.default_filedir = filedir - paths = [_f for _f in map(self.filedir_lookup, paths) if _f] - - result = None - for dir in paths: - if verbose: - verbose("looking for '%s' in '%s' ...\n" % (filename, dir)) - node, d = dir.srcdir_find_file(filename) - if node: - if verbose: - verbose("... FOUND '%s' in '%s'\n" % (filename, d)) - result = node - break - - memo_dict[memo_key] = result - - return result - -find_file = FileFinder().find_file - - -def invalidate_node_memos(targets): - """ - Invalidate the memoized values of all Nodes (files or directories) - that are associated with the given entries. Has been added to - clear the cache of nodes affected by a direct execution of an - action (e.g. Delete/Copy/Chmod). Existing Node caches become - inconsistent if the action is run through Execute(). The argument - `targets` can be a single Node object or filename, or a sequence - of Nodes/filenames. - """ - from traceback import extract_stack - - # First check if the cache really needs to be flushed. Only - # actions run in the SConscript with Execute() seem to be - # affected. XXX The way to check if Execute() is in the stacktrace - # is a very dirty hack and should be replaced by a more sensible - # solution. - for f in extract_stack(): - if f[2] == 'Execute' and f[0][-14:] == 'Environment.py': - break - else: - # Dont have to invalidate, so return - return - - if not SCons.Util.is_List(targets): - targets = [targets] - - for entry in targets: - # If the target is a Node object, clear the cache. If it is a - # filename, look up potentially existing Node object first. - try: - entry.clear_memoized_values() - except AttributeError: - # Not a Node object, try to look up Node by filename. XXX - # This creates Node objects even for those filenames which - # do not correspond to an existing Node object. - node = get_default_fs().Entry(entry) - if node: - node.clear_memoized_values() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Node/Python.py b/scons/scons-local-2.2.0/SCons/Node/Python.py deleted file mode 100644 index 5c302e6f7..000000000 --- a/scons/scons-local-2.2.0/SCons/Node/Python.py +++ /dev/null @@ -1,128 +0,0 @@ -"""scons.Node.Python - -Python nodes. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Node/Python.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Node - -class ValueNodeInfo(SCons.Node.NodeInfoBase): - current_version_id = 1 - - field_list = ['csig'] - - def str_to_node(self, s): - return Value(s) - -class ValueBuildInfo(SCons.Node.BuildInfoBase): - current_version_id = 1 - -class Value(SCons.Node.Node): - """A class for Python variables, typically passed on the command line - or generated by a script, but not from a file or some other source. - """ - - NodeInfo = ValueNodeInfo - BuildInfo = ValueBuildInfo - - def __init__(self, value, built_value=None): - SCons.Node.Node.__init__(self) - self.value = value - if built_value is not None: - self.built_value = built_value - - def str_for_display(self): - return repr(self.value) - - def __str__(self): - return str(self.value) - - def make_ready(self): - self.get_csig() - - def build(self, **kw): - if not hasattr(self, 'built_value'): - SCons.Node.Node.build(self, **kw) - - is_up_to_date = SCons.Node.Node.children_are_up_to_date - - def is_under(self, dir): - # Make Value nodes get built regardless of - # what directory scons was run from. Value nodes - # are outside the filesystem: - return 1 - - def write(self, built_value): - """Set the value of the node.""" - self.built_value = built_value - - def read(self): - """Return the value. If necessary, the value is built.""" - self.build() - if not hasattr(self, 'built_value'): - self.built_value = self.value - return self.built_value - - def get_text_contents(self): - """By the assumption that the node.built_value is a - deterministic product of the sources, the contents of a Value - are the concatenation of all the contents of its sources. As - the value need not be built when get_contents() is called, we - cannot use the actual node.built_value.""" - ###TODO: something reasonable about universal newlines - contents = str(self.value) - for kid in self.children(None): - contents = contents + kid.get_contents() - return contents - - get_contents = get_text_contents ###TODO should return 'bytes' value - - def changed_since_last_build(self, target, prev_ni): - cur_csig = self.get_csig() - try: - return cur_csig != prev_ni.csig - except AttributeError: - return 1 - - def get_csig(self, calc=None): - """Because we're a Python value node and don't have a real - timestamp, we get to ignore the calculator and just use the - value contents.""" - try: - return self.ninfo.csig - except AttributeError: - pass - contents = self.get_contents() - self.get_ninfo().csig = contents - return contents - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Node/__init__.py b/scons/scons-local-2.2.0/SCons/Node/__init__.py deleted file mode 100644 index 126d83c09..000000000 --- a/scons/scons-local-2.2.0/SCons/Node/__init__.py +++ /dev/null @@ -1,1330 +0,0 @@ -"""SCons.Node - -The Node package for the SCons software construction utility. - -This is, in many ways, the heart of SCons. - -A Node is where we encapsulate all of the dependency information about -any thing that SCons can build, or about any thing which SCons can use -to build some other thing. The canonical "thing," of course, is a file, -but a Node can also represent something remote (like a web page) or -something completely abstract (like an Alias). - -Each specific type of "thing" is specifically represented by a subclass -of the Node base class: Node.FS.File for files, Node.Alias for aliases, -etc. Dependency information is kept here in the base class, and -information specific to files/aliases/etc. is in the subclass. The -goal, if we've done this correctly, is that any type of "thing" should -be able to depend on any other type of "thing." - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Node/__init__.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import collections -import copy -from itertools import chain - -from SCons.Debug import logInstanceCreation -import SCons.Executor -import SCons.Memoize -import SCons.Util - -from SCons.Debug import Trace - -def classname(obj): - return str(obj.__class__).split('.')[-1] - -# Node states -# -# These are in "priority" order, so that the maximum value for any -# child/dependency of a node represents the state of that node if -# it has no builder of its own. The canonical example is a file -# system directory, which is only up to date if all of its children -# were up to date. -no_state = 0 -pending = 1 -executing = 2 -up_to_date = 3 -executed = 4 -failed = 5 - -StateString = { - 0 : "no_state", - 1 : "pending", - 2 : "executing", - 3 : "up_to_date", - 4 : "executed", - 5 : "failed", -} - -# controls whether implicit dependencies are cached: -implicit_cache = 0 - -# controls whether implicit dep changes are ignored: -implicit_deps_unchanged = 0 - -# controls whether the cached implicit deps are ignored: -implicit_deps_changed = 0 - -# A variable that can be set to an interface-specific function be called -# to annotate a Node with information about its creation. -def do_nothing(node): pass - -Annotate = do_nothing - -# Classes for signature info for Nodes. - -class NodeInfoBase(object): - """ - The generic base class for signature information for a Node. - - Node subclasses should subclass NodeInfoBase to provide their own - logic for dealing with their own Node-specific signature information. - """ - current_version_id = 1 - def __init__(self, node=None): - # Create an object attribute from the class attribute so it ends up - # in the pickled data in the .sconsign file. - self._version_id = self.current_version_id - def update(self, node): - try: - field_list = self.field_list - except AttributeError: - return - for f in field_list: - try: - delattr(self, f) - except AttributeError: - pass - try: - func = getattr(node, 'get_' + f) - except AttributeError: - pass - else: - setattr(self, f, func()) - def convert(self, node, val): - pass - def merge(self, other): - self.__dict__.update(other.__dict__) - def format(self, field_list=None, names=0): - if field_list is None: - try: - field_list = self.field_list - except AttributeError: - field_list = sorted(self.__dict__.keys()) - fields = [] - for field in field_list: - try: - f = getattr(self, field) - except AttributeError: - f = None - f = str(f) - if names: - f = field + ': ' + f - fields.append(f) - return fields - -class BuildInfoBase(object): - """ - The generic base class for build information for a Node. - - This is what gets stored in a .sconsign file for each target file. - It contains a NodeInfo instance for this node (signature information - that's specific to the type of Node) and direct attributes for the - generic build stuff we have to track: sources, explicit dependencies, - implicit dependencies, and action information. - """ - current_version_id = 1 - def __init__(self, node=None): - # Create an object attribute from the class attribute so it ends up - # in the pickled data in the .sconsign file. - self._version_id = self.current_version_id - self.bsourcesigs = [] - self.bdependsigs = [] - self.bimplicitsigs = [] - self.bactsig = None - def merge(self, other): - self.__dict__.update(other.__dict__) - -class Node(object): - """The base Node class, for entities that we know how to - build, or use to build other Nodes. - """ - - if SCons.Memoize.use_memoizer: - __metaclass__ = SCons.Memoize.Memoized_Metaclass - - memoizer_counters = [] - - class Attrs(object): - pass - - def __init__(self): - if __debug__: logInstanceCreation(self, 'Node.Node') - # Note that we no longer explicitly initialize a self.builder - # attribute to None here. That's because the self.builder - # attribute may be created on-the-fly later by a subclass (the - # canonical example being a builder to fetch a file from a - # source code system like CVS or Subversion). - - # Each list of children that we maintain is accompanied by a - # dictionary used to look up quickly whether a node is already - # present in the list. Empirical tests showed that it was - # fastest to maintain them as side-by-side Node attributes in - # this way, instead of wrapping up each list+dictionary pair in - # a class. (Of course, we could always still do that in the - # future if we had a good reason to...). - self.sources = [] # source files used to build node - self.sources_set = set() - self._specific_sources = False - self.depends = [] # explicit dependencies (from Depends) - self.depends_set = set() - self.ignore = [] # dependencies to ignore - self.ignore_set = set() - self.prerequisites = SCons.Util.UniqueList() - self.implicit = None # implicit (scanned) dependencies (None means not scanned yet) - self.waiting_parents = set() - self.waiting_s_e = set() - self.ref_count = 0 - self.wkids = None # Kids yet to walk, when it's an array - - self.env = None - self.state = no_state - self.precious = None - self.noclean = 0 - self.nocache = 0 - self.cached = 0 # is this node pulled from cache? - self.always_build = None - self.includes = None - self.attributes = self.Attrs() # Generic place to stick information about the Node. - self.side_effect = 0 # true iff this node is a side effect - self.side_effects = [] # the side effects of building this target - self.linked = 0 # is this node linked to the variant directory? - - self.clear_memoized_values() - - # Let the interface in which the build engine is embedded - # annotate this Node with its own info (like a description of - # what line in what file created the node, for example). - Annotate(self) - - def disambiguate(self, must_exist=None): - return self - - def get_suffix(self): - return '' - - memoizer_counters.append(SCons.Memoize.CountValue('get_build_env')) - - def get_build_env(self): - """Fetch the appropriate Environment to build this node. - """ - try: - return self._memo['get_build_env'] - except KeyError: - pass - result = self.get_executor().get_build_env() - self._memo['get_build_env'] = result - return result - - def get_build_scanner_path(self, scanner): - """Fetch the appropriate scanner path for this node.""" - return self.get_executor().get_build_scanner_path(scanner) - - def set_executor(self, executor): - """Set the action executor for this node.""" - self.executor = executor - - def get_executor(self, create=1): - """Fetch the action executor for this node. Create one if - there isn't already one, and requested to do so.""" - try: - executor = self.executor - except AttributeError: - if not create: - raise - try: - act = self.builder.action - except AttributeError: - executor = SCons.Executor.Null(targets=[self]) - else: - executor = SCons.Executor.Executor(act, - self.env or self.builder.env, - [self.builder.overrides], - [self], - self.sources) - self.executor = executor - return executor - - def executor_cleanup(self): - """Let the executor clean up any cached information.""" - try: - executor = self.get_executor(create=None) - except AttributeError: - pass - else: - executor.cleanup() - - def reset_executor(self): - "Remove cached executor; forces recompute when needed." - try: - delattr(self, 'executor') - except AttributeError: - pass - - def push_to_cache(self): - """Try to push a node into a cache - """ - pass - - def retrieve_from_cache(self): - """Try to retrieve the node's content from a cache - - This method is called from multiple threads in a parallel build, - so only do thread safe stuff here. Do thread unsafe stuff in - built(). - - Returns true if the node was successfully retrieved. - """ - return 0 - - # - # Taskmaster interface subsystem - # - - def make_ready(self): - """Get a Node ready for evaluation. - - This is called before the Taskmaster decides if the Node is - up-to-date or not. Overriding this method allows for a Node - subclass to be disambiguated if necessary, or for an implicit - source builder to be attached. - """ - pass - - def prepare(self): - """Prepare for this Node to be built. - - This is called after the Taskmaster has decided that the Node - is out-of-date and must be rebuilt, but before actually calling - the method to build the Node. - - This default implementation checks that explicit or implicit - dependencies either exist or are derived, and initializes the - BuildInfo structure that will hold the information about how - this node is, uh, built. - - (The existence of source files is checked separately by the - Executor, which aggregates checks for all of the targets built - by a specific action.) - - Overriding this method allows for for a Node subclass to remove - the underlying file from the file system. Note that subclass - methods should call this base class method to get the child - check and the BuildInfo structure. - """ - for d in self.depends: - if d.missing(): - msg = "Explicit dependency `%s' not found, needed by target `%s'." - raise SCons.Errors.StopError(msg % (d, self)) - if self.implicit is not None: - for i in self.implicit: - if i.missing(): - msg = "Implicit dependency `%s' not found, needed by target `%s'." - raise SCons.Errors.StopError(msg % (i, self)) - self.binfo = self.get_binfo() - - def build(self, **kw): - """Actually build the node. - - This is called by the Taskmaster after it's decided that the - Node is out-of-date and must be rebuilt, and after the prepare() - method has gotten everything, uh, prepared. - - This method is called from multiple threads in a parallel build, - so only do thread safe stuff here. Do thread unsafe stuff - in built(). - - """ - try: - self.get_executor()(self, **kw) - except SCons.Errors.BuildError, e: - e.node = self - raise - - def built(self): - """Called just after this node is successfully built.""" - - # Clear the implicit dependency caches of any Nodes - # waiting for this Node to be built. - for parent in self.waiting_parents: - parent.implicit = None - - self.clear() - - self.ninfo.update(self) - - def visited(self): - """Called just after this node has been visited (with or - without a build).""" - try: - binfo = self.binfo - except AttributeError: - # Apparently this node doesn't need build info, so - # don't bother calculating or storing it. - pass - else: - self.ninfo.update(self) - self.store_info() - - # - # - # - - def add_to_waiting_s_e(self, node): - self.waiting_s_e.add(node) - - def add_to_waiting_parents(self, node): - """ - Returns the number of nodes added to our waiting parents list: - 1 if we add a unique waiting parent, 0 if not. (Note that the - returned values are intended to be used to increment a reference - count, so don't think you can "clean up" this function by using - True and False instead...) - """ - wp = self.waiting_parents - if node in wp: - return 0 - wp.add(node) - return 1 - - def postprocess(self): - """Clean up anything we don't need to hang onto after we've - been built.""" - self.executor_cleanup() - self.waiting_parents = set() - - def clear(self): - """Completely clear a Node of all its cached state (so that it - can be re-evaluated by interfaces that do continuous integration - builds). - """ - # The del_binfo() call here isn't necessary for normal execution, - # but is for interactive mode, where we might rebuild the same - # target and need to start from scratch. - self.del_binfo() - self.clear_memoized_values() - self.ninfo = self.new_ninfo() - self.executor_cleanup() - try: - delattr(self, '_calculated_sig') - except AttributeError: - pass - self.includes = None - - def clear_memoized_values(self): - self._memo = {} - - def builder_set(self, builder): - self.builder = builder - try: - del self.executor - except AttributeError: - pass - - def has_builder(self): - """Return whether this Node has a builder or not. - - In Boolean tests, this turns out to be a *lot* more efficient - than simply examining the builder attribute directly ("if - node.builder: ..."). When the builder attribute is examined - directly, it ends up calling __getattr__ for both the __len__ - and __nonzero__ attributes on instances of our Builder Proxy - class(es), generating a bazillion extra calls and slowing - things down immensely. - """ - try: - b = self.builder - except AttributeError: - # There was no explicit builder for this Node, so initialize - # the self.builder attribute to None now. - b = self.builder = None - return b is not None - - def set_explicit(self, is_explicit): - self.is_explicit = is_explicit - - def has_explicit_builder(self): - """Return whether this Node has an explicit builder - - This allows an internal Builder created by SCons to be marked - non-explicit, so that it can be overridden by an explicit - builder that the user supplies (the canonical example being - directories).""" - try: - return self.is_explicit - except AttributeError: - self.is_explicit = None - return self.is_explicit - - def get_builder(self, default_builder=None): - """Return the set builder, or a specified default value""" - try: - return self.builder - except AttributeError: - return default_builder - - multiple_side_effect_has_builder = has_builder - - def is_derived(self): - """ - Returns true iff this node is derived (i.e. built). - - This should return true only for nodes whose path should be in - the variant directory when duplicate=0 and should contribute their build - signatures when they are used as source files to other derived files. For - example: source with source builders are not derived in this sense, - and hence should not return true. - """ - return self.has_builder() or self.side_effect - - def alter_targets(self): - """Return a list of alternate targets for this Node. - """ - return [], None - - def get_found_includes(self, env, scanner, path): - """Return the scanned include lines (implicit dependencies) - found in this node. - - The default is no implicit dependencies. We expect this method - to be overridden by any subclass that can be scanned for - implicit dependencies. - """ - return [] - - def get_implicit_deps(self, env, scanner, path): - """Return a list of implicit dependencies for this node. - - This method exists to handle recursive invocation of the scanner - on the implicit dependencies returned by the scanner, if the - scanner's recursive flag says that we should. - """ - if not scanner: - return [] - - # Give the scanner a chance to select a more specific scanner - # for this Node. - #scanner = scanner.select(self) - - nodes = [self] - seen = {} - seen[self] = 1 - deps = [] - while nodes: - n = nodes.pop(0) - d = [x for x in n.get_found_includes(env, scanner, path) if x not in seen] - if d: - deps.extend(d) - for n in d: - seen[n] = 1 - nodes.extend(scanner.recurse_nodes(d)) - - return deps - - def get_env_scanner(self, env, kw={}): - return env.get_scanner(self.scanner_key()) - - def get_target_scanner(self): - return self.builder.target_scanner - - def get_source_scanner(self, node): - """Fetch the source scanner for the specified node - - NOTE: "self" is the target being built, "node" is - the source file for which we want to fetch the scanner. - - Implies self.has_builder() is true; again, expect to only be - called from locations where this is already verified. - - This function may be called very often; it attempts to cache - the scanner found to improve performance. - """ - scanner = None - try: - scanner = self.builder.source_scanner - except AttributeError: - pass - if not scanner: - # The builder didn't have an explicit scanner, so go look up - # a scanner from env['SCANNERS'] based on the node's scanner - # key (usually the file extension). - scanner = self.get_env_scanner(self.get_build_env()) - if scanner: - scanner = scanner.select(node) - return scanner - - def add_to_implicit(self, deps): - if not hasattr(self, 'implicit') or self.implicit is None: - self.implicit = [] - self.implicit_set = set() - self._children_reset() - self._add_child(self.implicit, self.implicit_set, deps) - - def scan(self): - """Scan this node's dependents for implicit dependencies.""" - # Don't bother scanning non-derived files, because we don't - # care what their dependencies are. - # Don't scan again, if we already have scanned. - if self.implicit is not None: - return - self.implicit = [] - self.implicit_set = set() - self._children_reset() - if not self.has_builder(): - return - - build_env = self.get_build_env() - executor = self.get_executor() - - # Here's where we implement --implicit-cache. - if implicit_cache and not implicit_deps_changed: - implicit = self.get_stored_implicit() - if implicit is not None: - # We now add the implicit dependencies returned from the - # stored .sconsign entry to have already been converted - # to Nodes for us. (We used to run them through a - # source_factory function here.) - - # Update all of the targets with them. This - # essentially short-circuits an N*M scan of the - # sources for each individual target, which is a hell - # of a lot more efficient. - for tgt in executor.get_all_targets(): - tgt.add_to_implicit(implicit) - - if implicit_deps_unchanged or self.is_up_to_date(): - return - # one of this node's sources has changed, - # so we must recalculate the implicit deps for all targets - for tgt in executor.get_all_targets(): - tgt.implicit = [] - tgt.implicit_set = set() - - # Have the executor scan the sources. - executor.scan_sources(self.builder.source_scanner) - - # If there's a target scanner, have the executor scan the target - # node itself and associated targets that might be built. - scanner = self.get_target_scanner() - if scanner: - executor.scan_targets(scanner) - - def scanner_key(self): - return None - - def select_scanner(self, scanner): - """Selects a scanner for this Node. - - This is a separate method so it can be overridden by Node - subclasses (specifically, Node.FS.Dir) that *must* use their - own Scanner and don't select one the Scanner.Selector that's - configured for the target. - """ - return scanner.select(self) - - def env_set(self, env, safe=0): - if safe and self.env: - return - self.env = env - - # - # SIGNATURE SUBSYSTEM - # - - NodeInfo = NodeInfoBase - BuildInfo = BuildInfoBase - - def new_ninfo(self): - ninfo = self.NodeInfo(self) - return ninfo - - def get_ninfo(self): - try: - return self.ninfo - except AttributeError: - self.ninfo = self.new_ninfo() - return self.ninfo - - def new_binfo(self): - binfo = self.BuildInfo(self) - return binfo - - def get_binfo(self): - """ - Fetch a node's build information. - - node - the node whose sources will be collected - cache - alternate node to use for the signature cache - returns - the build signature - - This no longer handles the recursive descent of the - node's children's signatures. We expect that they're - already built and updated by someone else, if that's - what's wanted. - """ - try: - return self.binfo - except AttributeError: - pass - - binfo = self.new_binfo() - self.binfo = binfo - - executor = self.get_executor() - ignore_set = self.ignore_set - - if self.has_builder(): - binfo.bact = str(executor) - binfo.bactsig = SCons.Util.MD5signature(executor.get_contents()) - - if self._specific_sources: - sources = [] - for s in self.sources: - if s not in ignore_set: - sources.append(s) - else: - sources = executor.get_unignored_sources(self, self.ignore) - seen = set() - bsources = [] - bsourcesigs = [] - for s in sources: - if not s in seen: - seen.add(s) - bsources.append(s) - bsourcesigs.append(s.get_ninfo()) - binfo.bsources = bsources - binfo.bsourcesigs = bsourcesigs - - depends = self.depends - dependsigs = [] - for d in depends: - if d not in ignore_set: - dependsigs.append(d.get_ninfo()) - binfo.bdepends = depends - binfo.bdependsigs = dependsigs - - implicit = self.implicit or [] - implicitsigs = [] - for i in implicit: - if i not in ignore_set: - implicitsigs.append(i.get_ninfo()) - binfo.bimplicit = implicit - binfo.bimplicitsigs = implicitsigs - - return binfo - - def del_binfo(self): - """Delete the build info from this node.""" - try: - delattr(self, 'binfo') - except AttributeError: - pass - - def get_csig(self): - try: - return self.ninfo.csig - except AttributeError: - ninfo = self.get_ninfo() - ninfo.csig = SCons.Util.MD5signature(self.get_contents()) - return self.ninfo.csig - - def get_cachedir_csig(self): - return self.get_csig() - - def store_info(self): - """Make the build signature permanent (that is, store it in the - .sconsign file or equivalent).""" - pass - - def do_not_store_info(self): - pass - - def get_stored_info(self): - return None - - def get_stored_implicit(self): - """Fetch the stored implicit dependencies""" - return None - - # - # - # - - def set_precious(self, precious = 1): - """Set the Node's precious value.""" - self.precious = precious - - def set_noclean(self, noclean = 1): - """Set the Node's noclean value.""" - # Make sure noclean is an integer so the --debug=stree - # output in Util.py can use it as an index. - self.noclean = noclean and 1 or 0 - - def set_nocache(self, nocache = 1): - """Set the Node's nocache value.""" - # Make sure nocache is an integer so the --debug=stree - # output in Util.py can use it as an index. - self.nocache = nocache and 1 or 0 - - def set_always_build(self, always_build = 1): - """Set the Node's always_build value.""" - self.always_build = always_build - - def exists(self): - """Does this node exists?""" - # All node exist by default: - return 1 - - def rexists(self): - """Does this node exist locally or in a repositiory?""" - # There are no repositories by default: - return self.exists() - - def missing(self): - return not self.is_derived() and \ - not self.linked and \ - not self.rexists() - - def remove(self): - """Remove this Node: no-op by default.""" - return None - - def add_dependency(self, depend): - """Adds dependencies.""" - try: - self._add_child(self.depends, self.depends_set, depend) - except TypeError, e: - e = e.args[0] - if SCons.Util.is_List(e): - s = list(map(str, e)) - else: - s = str(e) - raise SCons.Errors.UserError("attempted to add a non-Node dependency to %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e))) - - def add_prerequisite(self, prerequisite): - """Adds prerequisites""" - self.prerequisites.extend(prerequisite) - self._children_reset() - - def add_ignore(self, depend): - """Adds dependencies to ignore.""" - try: - self._add_child(self.ignore, self.ignore_set, depend) - except TypeError, e: - e = e.args[0] - if SCons.Util.is_List(e): - s = list(map(str, e)) - else: - s = str(e) - raise SCons.Errors.UserError("attempted to ignore a non-Node dependency of %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e))) - - def add_source(self, source): - """Adds sources.""" - if self._specific_sources: - return - try: - self._add_child(self.sources, self.sources_set, source) - except TypeError, e: - e = e.args[0] - if SCons.Util.is_List(e): - s = list(map(str, e)) - else: - s = str(e) - raise SCons.Errors.UserError("attempted to add a non-Node as source of %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e))) - - def _add_child(self, collection, set, child): - """Adds 'child' to 'collection', first checking 'set' to see if it's - already present.""" - #if type(child) is not type([]): - # child = [child] - #for c in child: - # if not isinstance(c, Node): - # raise TypeError, c - added = None - for c in child: - if c not in set: - set.add(c) - collection.append(c) - added = 1 - if added: - self._children_reset() - - def set_specific_source(self, source): - self.add_source(source) - self._specific_sources = True - - def add_wkid(self, wkid): - """Add a node to the list of kids waiting to be evaluated""" - if self.wkids is not None: - self.wkids.append(wkid) - - def _children_reset(self): - self.clear_memoized_values() - # We need to let the Executor clear out any calculated - # build info that it's cached so we can re-calculate it. - self.executor_cleanup() - - memoizer_counters.append(SCons.Memoize.CountValue('_children_get')) - - def _children_get(self): - try: - return self._memo['children_get'] - except KeyError: - pass - - # The return list may contain duplicate Nodes, especially in - # source trees where there are a lot of repeated #includes - # of a tangle of .h files. Profiling shows, however, that - # eliminating the duplicates with a brute-force approach that - # preserves the order (that is, something like: - # - # u = [] - # for n in list: - # if n not in u: - # u.append(n)" - # - # takes more cycles than just letting the underlying methods - # hand back cached values if a Node's information is requested - # multiple times. (Other methods of removing duplicates, like - # using dictionary keys, lose the order, and the only ordered - # dictionary patterns I found all ended up using "not in" - # internally anyway...) - if self.ignore_set: - if self.implicit is None: - iter = chain(self.sources,self.depends) - else: - iter = chain(self.sources, self.depends, self.implicit) - - children = [] - for i in iter: - if i not in self.ignore_set: - children.append(i) - else: - if self.implicit is None: - children = self.sources + self.depends - else: - children = self.sources + self.depends + self.implicit - - self._memo['children_get'] = children - return children - - def all_children(self, scan=1): - """Return a list of all the node's direct children.""" - if scan: - self.scan() - - # The return list may contain duplicate Nodes, especially in - # source trees where there are a lot of repeated #includes - # of a tangle of .h files. Profiling shows, however, that - # eliminating the duplicates with a brute-force approach that - # preserves the order (that is, something like: - # - # u = [] - # for n in list: - # if n not in u: - # u.append(n)" - # - # takes more cycles than just letting the underlying methods - # hand back cached values if a Node's information is requested - # multiple times. (Other methods of removing duplicates, like - # using dictionary keys, lose the order, and the only ordered - # dictionary patterns I found all ended up using "not in" - # internally anyway...) - if self.implicit is None: - return self.sources + self.depends - else: - return self.sources + self.depends + self.implicit - - def children(self, scan=1): - """Return a list of the node's direct children, minus those - that are ignored by this node.""" - if scan: - self.scan() - return self._children_get() - - def set_state(self, state): - self.state = state - - def get_state(self): - return self.state - - def state_has_changed(self, target, prev_ni): - return (self.state != SCons.Node.up_to_date) - - def get_env(self): - env = self.env - if not env: - import SCons.Defaults - env = SCons.Defaults.DefaultEnvironment() - return env - - def changed_since_last_build(self, target, prev_ni): - """ - - Must be overridden in a specific subclass to return True if this - Node (a dependency) has changed since the last time it was used - to build the specified target. prev_ni is this Node's state (for - example, its file timestamp, length, maybe content signature) - as of the last time the target was built. - - Note that this method is called through the dependency, not the - target, because a dependency Node must be able to use its own - logic to decide if it changed. For example, File Nodes need to - obey if we're configured to use timestamps, but Python Value Nodes - never use timestamps and always use the content. If this method - were called through the target, then each Node's implementation - of this method would have to have more complicated logic to - handle all the different Node types on which it might depend. - """ - raise NotImplementedError - - def Decider(self, function): - SCons.Util.AddMethod(self, function, 'changed_since_last_build') - - def changed(self, node=None): - """ - Returns if the node is up-to-date with respect to the BuildInfo - stored last time it was built. The default behavior is to compare - it against our own previously stored BuildInfo, but the stored - BuildInfo from another Node (typically one in a Repository) - can be used instead. - - Note that we now *always* check every dependency. We used to - short-circuit the check by returning as soon as we detected - any difference, but we now rely on checking every dependency - to make sure that any necessary Node information (for example, - the content signature of an #included .h file) is updated. - """ - t = 0 - if t: Trace('changed(%s [%s], %s)' % (self, classname(self), node)) - if node is None: - node = self - - result = False - - bi = node.get_stored_info().binfo - then = bi.bsourcesigs + bi.bdependsigs + bi.bimplicitsigs - children = self.children() - - diff = len(children) - len(then) - if diff: - # The old and new dependency lists are different lengths. - # This always indicates that the Node must be rebuilt. - # We also extend the old dependency list with enough None - # entries to equal the new dependency list, for the benefit - # of the loop below that updates node information. - then.extend([None] * diff) - if t: Trace(': old %s new %s' % (len(then), len(children))) - result = True - - for child, prev_ni in zip(children, then): - if child.changed_since_last_build(self, prev_ni): - if t: Trace(': %s changed' % child) - result = True - - contents = self.get_executor().get_contents() - if self.has_builder(): - import SCons.Util - newsig = SCons.Util.MD5signature(contents) - if bi.bactsig != newsig: - if t: Trace(': bactsig %s != newsig %s' % (bi.bactsig, newsig)) - result = True - - if not result: - if t: Trace(': up to date') - - if t: Trace('\n') - - return result - - def is_up_to_date(self): - """Default check for whether the Node is current: unknown Node - subtypes are always out of date, so they will always get built.""" - return None - - def children_are_up_to_date(self): - """Alternate check for whether the Node is current: If all of - our children were up-to-date, then this Node was up-to-date, too. - - The SCons.Node.Alias and SCons.Node.Python.Value subclasses - rebind their current() method to this method.""" - # Allow the children to calculate their signatures. - self.binfo = self.get_binfo() - if self.always_build: - return None - state = 0 - for kid in self.children(None): - s = kid.get_state() - if s and (not state or s > state): - state = s - return (state == 0 or state == SCons.Node.up_to_date) - - def is_literal(self): - """Always pass the string representation of a Node to - the command interpreter literally.""" - return 1 - - def render_include_tree(self): - """ - Return a text representation, suitable for displaying to the - user, of the include tree for the sources of this node. - """ - if self.is_derived() and self.env: - env = self.get_build_env() - for s in self.sources: - scanner = self.get_source_scanner(s) - if scanner: - path = self.get_build_scanner_path(scanner) - else: - path = None - def f(node, env=env, scanner=scanner, path=path): - return node.get_found_includes(env, scanner, path) - return SCons.Util.render_tree(s, f, 1) - else: - return None - - def get_abspath(self): - """ - Return an absolute path to the Node. This will return simply - str(Node) by default, but for Node types that have a concept of - relative path, this might return something different. - """ - return str(self) - - def for_signature(self): - """ - Return a string representation of the Node that will always - be the same for this particular Node, no matter what. This - is by contrast to the __str__() method, which might, for - instance, return a relative path for a file Node. The purpose - of this method is to generate a value to be used in signature - calculation for the command line used to build a target, and - we use this method instead of str() to avoid unnecessary - rebuilds. This method does not need to return something that - would actually work in a command line; it can return any kind of - nonsense, so long as it does not change. - """ - return str(self) - - def get_string(self, for_signature): - """This is a convenience function designed primarily to be - used in command generators (i.e., CommandGeneratorActions or - Environment variables that are callable), which are called - with a for_signature argument that is nonzero if the command - generator is being called to generate a signature for the - command line, which determines if we should rebuild or not. - - Such command generators should use this method in preference - to str(Node) when converting a Node to a string, passing - in the for_signature parameter, such that we will call - Node.for_signature() or str(Node) properly, depending on whether - we are calculating a signature or actually constructing a - command line.""" - if for_signature: - return self.for_signature() - return str(self) - - def get_subst_proxy(self): - """ - This method is expected to return an object that will function - exactly like this Node, except that it implements any additional - special features that we would like to be in effect for - Environment variable substitution. The principle use is that - some Nodes would like to implement a __getattr__() method, - but putting that in the Node type itself has a tendency to kill - performance. We instead put it in a proxy and return it from - this method. It is legal for this method to return self - if no new functionality is needed for Environment substitution. - """ - return self - - def explain(self): - if not self.exists(): - return "building `%s' because it doesn't exist\n" % self - - if self.always_build: - return "rebuilding `%s' because AlwaysBuild() is specified\n" % self - - old = self.get_stored_info() - if old is None: - return None - - old = old.binfo - old.prepare_dependencies() - - try: - old_bkids = old.bsources + old.bdepends + old.bimplicit - old_bkidsigs = old.bsourcesigs + old.bdependsigs + old.bimplicitsigs - except AttributeError: - return "Cannot explain why `%s' is being rebuilt: No previous build information found\n" % self - - new = self.get_binfo() - - new_bkids = new.bsources + new.bdepends + new.bimplicit - new_bkidsigs = new.bsourcesigs + new.bdependsigs + new.bimplicitsigs - - osig = dict(zip(old_bkids, old_bkidsigs)) - nsig = dict(zip(new_bkids, new_bkidsigs)) - - # The sources and dependencies we'll want to report are all stored - # as relative paths to this target's directory, but we want to - # report them relative to the top-level SConstruct directory, - # so we only print them after running them through this lambda - # to turn them into the right relative Node and then return - # its string. - def stringify( s, E=self.dir.Entry ) : - if hasattr( s, 'dir' ) : - return str(E(s)) - return str(s) - - lines = [] - - removed = [x for x in old_bkids if not x in new_bkids] - if removed: - removed = list(map(stringify, removed)) - fmt = "`%s' is no longer a dependency\n" - lines.extend([fmt % s for s in removed]) - - for k in new_bkids: - if not k in old_bkids: - lines.append("`%s' is a new dependency\n" % stringify(k)) - elif k.changed_since_last_build(self, osig[k]): - lines.append("`%s' changed\n" % stringify(k)) - - if len(lines) == 0 and old_bkids != new_bkids: - lines.append("the dependency order changed:\n" + - "%sold: %s\n" % (' '*15, list(map(stringify, old_bkids))) + - "%snew: %s\n" % (' '*15, list(map(stringify, new_bkids)))) - - if len(lines) == 0: - def fmt_with_title(title, strlines): - lines = strlines.split('\n') - sep = '\n' + ' '*(15 + len(title)) - return ' '*15 + title + sep.join(lines) + '\n' - if old.bactsig != new.bactsig: - if old.bact == new.bact: - lines.append("the contents of the build action changed\n" + - fmt_with_title('action: ', new.bact)) - else: - lines.append("the build action changed:\n" + - fmt_with_title('old: ', old.bact) + - fmt_with_title('new: ', new.bact)) - - if len(lines) == 0: - return "rebuilding `%s' for unknown reasons\n" % self - - preamble = "rebuilding `%s' because" % self - if len(lines) == 1: - return "%s %s" % (preamble, lines[0]) - else: - lines = ["%s:\n" % preamble] + lines - return ( ' '*11).join(lines) - -class NodeList(collections.UserList): - def __str__(self): - return str(list(map(str, self.data))) - -def get_children(node, parent): return node.children() -def ignore_cycle(node, stack): pass -def do_nothing(node, parent): pass - -class Walker(object): - """An iterator for walking a Node tree. - - This is depth-first, children are visited before the parent. - The Walker object can be initialized with any node, and - returns the next node on the descent with each get_next() call. - 'kids_func' is an optional function that will be called to - get the children of a node instead of calling 'children'. - 'cycle_func' is an optional function that will be called - when a cycle is detected. - - This class does not get caught in node cycles caused, for example, - by C header file include loops. - """ - def __init__(self, node, kids_func=get_children, - cycle_func=ignore_cycle, - eval_func=do_nothing): - self.kids_func = kids_func - self.cycle_func = cycle_func - self.eval_func = eval_func - node.wkids = copy.copy(kids_func(node, None)) - self.stack = [node] - self.history = {} # used to efficiently detect and avoid cycles - self.history[node] = None - - def get_next(self): - """Return the next node for this walk of the tree. - - This function is intentionally iterative, not recursive, - to sidestep any issues of stack size limitations. - """ - - while self.stack: - if self.stack[-1].wkids: - node = self.stack[-1].wkids.pop(0) - if not self.stack[-1].wkids: - self.stack[-1].wkids = None - if node in self.history: - self.cycle_func(node, self.stack) - else: - node.wkids = copy.copy(self.kids_func(node, self.stack[-1])) - self.stack.append(node) - self.history[node] = None - else: - node = self.stack.pop() - del self.history[node] - if node: - if self.stack: - parent = self.stack[-1] - else: - parent = None - self.eval_func(node, parent) - return node - return None - - def is_done(self): - return not self.stack - - -arg2nodes_lookups = [] - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Options/BoolOption.py b/scons/scons-local-2.2.0/SCons/Options/BoolOption.py deleted file mode 100644 index be2de6ce7..000000000 --- a/scons/scons-local-2.2.0/SCons/Options/BoolOption.py +++ /dev/null @@ -1,50 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Options/BoolOption.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -__doc__ = """Place-holder for the old SCons.Options module hierarchy - -This is for backwards compatibility. The new equivalent is the Variables/ -class hierarchy. These will have deprecation warnings added (some day), -and will then be removed entirely (some day). -""" - -import SCons.Variables -import SCons.Warnings - -warned = False - -def BoolOption(*args, **kw): - global warned - if not warned: - msg = "The BoolOption() function is deprecated; use the BoolVariable() function instead." - SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) - warned = True - return SCons.Variables.BoolVariable(*args, **kw) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Options/EnumOption.py b/scons/scons-local-2.2.0/SCons/Options/EnumOption.py deleted file mode 100644 index b7aa8530e..000000000 --- a/scons/scons-local-2.2.0/SCons/Options/EnumOption.py +++ /dev/null @@ -1,50 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Options/EnumOption.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -__doc__ = """Place-holder for the old SCons.Options module hierarchy - -This is for backwards compatibility. The new equivalent is the Variables/ -class hierarchy. These will have deprecation warnings added (some day), -and will then be removed entirely (some day). -""" - -import SCons.Variables -import SCons.Warnings - -warned = False - -def EnumOption(*args, **kw): - global warned - if not warned: - msg = "The EnumOption() function is deprecated; use the EnumVariable() function instead." - SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) - warned = True - return SCons.Variables.EnumVariable(*args, **kw) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Options/ListOption.py b/scons/scons-local-2.2.0/SCons/Options/ListOption.py deleted file mode 100644 index ba4c4a98a..000000000 --- a/scons/scons-local-2.2.0/SCons/Options/ListOption.py +++ /dev/null @@ -1,50 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Options/ListOption.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -__doc__ = """Place-holder for the old SCons.Options module hierarchy - -This is for backwards compatibility. The new equivalent is the Variables/ -class hierarchy. These will have deprecation warnings added (some day), -and will then be removed entirely (some day). -""" - -import SCons.Variables -import SCons.Warnings - -warned = False - -def ListOption(*args, **kw): - global warned - if not warned: - msg = "The ListOption() function is deprecated; use the ListVariable() function instead." - SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) - warned = True - return SCons.Variables.ListVariable(*args, **kw) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Options/PackageOption.py b/scons/scons-local-2.2.0/SCons/Options/PackageOption.py deleted file mode 100644 index d65564077..000000000 --- a/scons/scons-local-2.2.0/SCons/Options/PackageOption.py +++ /dev/null @@ -1,50 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Options/PackageOption.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -__doc__ = """Place-holder for the old SCons.Options module hierarchy - -This is for backwards compatibility. The new equivalent is the Variables/ -class hierarchy. These will have deprecation warnings added (some day), -and will then be removed entirely (some day). -""" - -import SCons.Variables -import SCons.Warnings - -warned = False - -def PackageOption(*args, **kw): - global warned - if not warned: - msg = "The PackageOption() function is deprecated; use the PackageVariable() function instead." - SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) - warned = True - return SCons.Variables.PackageVariable(*args, **kw) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Options/PathOption.py b/scons/scons-local-2.2.0/SCons/Options/PathOption.py deleted file mode 100644 index e7de97f89..000000000 --- a/scons/scons-local-2.2.0/SCons/Options/PathOption.py +++ /dev/null @@ -1,76 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Options/PathOption.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -__doc__ = """Place-holder for the old SCons.Options module hierarchy - -This is for backwards compatibility. The new equivalent is the Variables/ -class hierarchy. These will have deprecation warnings added (some day), -and will then be removed entirely (some day). -""" - -import SCons.Variables -import SCons.Warnings - -warned = False - -class _PathOptionClass(object): - def warn(self): - global warned - if not warned: - msg = "The PathOption() function is deprecated; use the PathVariable() function instead." - SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) - warned = True - - def __call__(self, *args, **kw): - self.warn() - return SCons.Variables.PathVariable(*args, **kw) - - def PathAccept(self, *args, **kw): - self.warn() - return SCons.Variables.PathVariable.PathAccept(*args, **kw) - - def PathIsDir(self, *args, **kw): - self.warn() - return SCons.Variables.PathVariable.PathIsDir(*args, **kw) - - def PathIsDirCreate(self, *args, **kw): - self.warn() - return SCons.Variables.PathVariable.PathIsDirCreate(*args, **kw) - - def PathIsFile(self, *args, **kw): - self.warn() - return SCons.Variables.PathVariable.PathIsFile(*args, **kw) - - def PathExists(self, *args, **kw): - self.warn() - return SCons.Variables.PathVariable.PathExists(*args, **kw) - -PathOption = _PathOptionClass() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Options/__init__.py b/scons/scons-local-2.2.0/SCons/Options/__init__.py deleted file mode 100644 index 62edfa900..000000000 --- a/scons/scons-local-2.2.0/SCons/Options/__init__.py +++ /dev/null @@ -1,67 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Options/__init__.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -__doc__ = """Place-holder for the old SCons.Options module hierarchy - -This is for backwards compatibility. The new equivalent is the Variables/ -class hierarchy. These will have deprecation warnings added (some day), -and will then be removed entirely (some day). -""" - -import SCons.Variables -import SCons.Warnings - -from BoolOption import BoolOption # okay -from EnumOption import EnumOption # okay -from ListOption import ListOption # naja -from PackageOption import PackageOption # naja -from PathOption import PathOption # okay - -warned = False - -class Options(SCons.Variables.Variables): - def __init__(self, *args, **kw): - global warned - if not warned: - msg = "The Options class is deprecated; use the Variables class instead." - SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) - warned = True - SCons.Variables.Variables.__init__(self, *args, **kw) - - def AddOptions(self, *args, **kw): - return SCons.Variables.Variables.AddVariables(self, *args, **kw) - - def UnknownOptions(self, *args, **kw): - return SCons.Variables.Variables.UnknownVariables(self, *args, **kw) - - def FormatOptionHelpText(self, *args, **kw): - return SCons.Variables.Variables.FormatVariableHelpText(self, *args, - **kw) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/PathList.py b/scons/scons-local-2.2.0/SCons/PathList.py deleted file mode 100644 index 9a4145331..000000000 --- a/scons/scons-local-2.2.0/SCons/PathList.py +++ /dev/null @@ -1,231 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/PathList.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -__doc__ = """SCons.PathList - -A module for handling lists of directory paths (the sort of things -that get set as CPPPATH, LIBPATH, etc.) with as much caching of data and -efficiency as we can while still keeping the evaluation delayed so that we -Do the Right Thing (almost) regardless of how the variable is specified. - -""" - -import os - -import SCons.Memoize -import SCons.Node -import SCons.Util - -# -# Variables to specify the different types of entries in a PathList object: -# - -TYPE_STRING_NO_SUBST = 0 # string with no '$' -TYPE_STRING_SUBST = 1 # string containing '$' -TYPE_OBJECT = 2 # other object - -def node_conv(obj): - """ - This is the "string conversion" routine that we have our substitutions - use to return Nodes, not strings. This relies on the fact that an - EntryProxy object has a get() method that returns the underlying - Node that it wraps, which is a bit of architectural dependence - that we might need to break or modify in the future in response to - additional requirements. - """ - try: - get = obj.get - except AttributeError: - if isinstance(obj, SCons.Node.Node) or SCons.Util.is_Sequence( obj ): - result = obj - else: - result = str(obj) - else: - result = get() - return result - -class _PathList(object): - """ - An actual PathList object. - """ - def __init__(self, pathlist): - """ - Initializes a PathList object, canonicalizing the input and - pre-processing it for quicker substitution later. - - The stored representation of the PathList is a list of tuples - containing (type, value), where the "type" is one of the TYPE_* - variables defined above. We distinguish between: - - strings that contain no '$' and therefore need no - delayed-evaluation string substitution (we expect that there - will be many of these and that we therefore get a pretty - big win from avoiding string substitution) - - strings that contain '$' and therefore need substitution - (the hard case is things like '${TARGET.dir}/include', - which require re-evaluation for every target + source) - - other objects (which may be something like an EntryProxy - that needs a method called to return a Node) - - Pre-identifying the type of each element in the PathList up-front - and storing the type in the list of tuples is intended to reduce - the amount of calculation when we actually do the substitution - over and over for each target. - """ - if SCons.Util.is_String(pathlist): - pathlist = pathlist.split(os.pathsep) - elif not SCons.Util.is_Sequence(pathlist): - pathlist = [pathlist] - - pl = [] - for p in pathlist: - try: - index = p.find('$') - except (AttributeError, TypeError): - type = TYPE_OBJECT - else: - if index == -1: - type = TYPE_STRING_NO_SUBST - else: - type = TYPE_STRING_SUBST - pl.append((type, p)) - - self.pathlist = tuple(pl) - - def __len__(self): return len(self.pathlist) - - def __getitem__(self, i): return self.pathlist[i] - - def subst_path(self, env, target, source): - """ - Performs construction variable substitution on a pre-digested - PathList for a specific target and source. - """ - result = [] - for type, value in self.pathlist: - if type == TYPE_STRING_SUBST: - value = env.subst(value, target=target, source=source, - conv=node_conv) - if SCons.Util.is_Sequence(value): - result.extend(value) - continue - - elif type == TYPE_OBJECT: - value = node_conv(value) - if value: - result.append(value) - return tuple(result) - - -class PathListCache(object): - """ - A class to handle caching of PathList lookups. - - This class gets instantiated once and then deleted from the namespace, - so it's used as a Singleton (although we don't enforce that in the - usual Pythonic ways). We could have just made the cache a dictionary - in the module namespace, but putting it in this class allows us to - use the same Memoizer pattern that we use elsewhere to count cache - hits and misses, which is very valuable. - - Lookup keys in the cache are computed by the _PathList_key() method. - Cache lookup should be quick, so we don't spend cycles canonicalizing - all forms of the same lookup key. For example, 'x:y' and ['x', - 'y'] logically represent the same list, but we don't bother to - split string representations and treat those two equivalently. - (Note, however, that we do, treat lists and tuples the same.) - - The main type of duplication we're trying to catch will come from - looking up the same path list from two different clones of the - same construction environment. That is, given - - env2 = env1.Clone() - - both env1 and env2 will have the same CPPPATH value, and we can - cheaply avoid re-parsing both values of CPPPATH by using the - common value from this cache. - """ - if SCons.Memoize.use_memoizer: - __metaclass__ = SCons.Memoize.Memoized_Metaclass - - memoizer_counters = [] - - def __init__(self): - self._memo = {} - - def _PathList_key(self, pathlist): - """ - Returns the key for memoization of PathLists. - - Note that we want this to be pretty quick, so we don't completely - canonicalize all forms of the same list. For example, - 'dir1:$ROOT/dir2' and ['$ROOT/dir1', 'dir'] may logically - represent the same list if you're executing from $ROOT, but - we're not going to bother splitting strings into path elements, - or massaging strings into Nodes, to identify that equivalence. - We just want to eliminate obvious redundancy from the normal - case of re-using exactly the same cloned value for a path. - """ - if SCons.Util.is_Sequence(pathlist): - pathlist = tuple(SCons.Util.flatten(pathlist)) - return pathlist - - memoizer_counters.append(SCons.Memoize.CountDict('PathList', _PathList_key)) - - def PathList(self, pathlist): - """ - Returns the cached _PathList object for the specified pathlist, - creating and caching a new object as necessary. - """ - pathlist = self._PathList_key(pathlist) - try: - memo_dict = self._memo['PathList'] - except KeyError: - memo_dict = {} - self._memo['PathList'] = memo_dict - else: - try: - return memo_dict[pathlist] - except KeyError: - pass - - result = _PathList(pathlist) - - memo_dict[pathlist] = result - - return result - -PathList = PathListCache().PathList - - -del PathListCache - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Platform/__init__.py b/scons/scons-local-2.2.0/SCons/Platform/__init__.py deleted file mode 100644 index 2cab3c865..000000000 --- a/scons/scons-local-2.2.0/SCons/Platform/__init__.py +++ /dev/null @@ -1,241 +0,0 @@ -"""SCons.Platform - -SCons platform selection. - -This looks for modules that define a callable object that can modify a -construction environment as appropriate for a given platform. - -Note that we take a more simplistic view of "platform" than Python does. -We're looking for a single string that determines a set of -tool-independent variables with which to initialize a construction -environment. Consequently, we'll examine both sys.platform and os.name -(and anything else that might come in to play) in order to return some -specification which is unique enough for our purposes. - -Note that because this subsysem just *selects* a callable that can -modify a construction environment, it's possible for people to define -their own "platform specification" in an arbitrary callable function. -No one needs to use or tie in to this subsystem in order to roll -their own platform definition. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Platform/__init__.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.compat - -import imp -import os -import sys -import tempfile - -import SCons.Errors -import SCons.Subst -import SCons.Tool - -def platform_default(): - """Return the platform string for our execution environment. - - The returned value should map to one of the SCons/Platform/*.py - files. Since we're architecture independent, though, we don't - care about the machine architecture. - """ - osname = os.name - if osname == 'java': - osname = os._osType - if osname == 'posix': - if sys.platform == 'cygwin': - return 'cygwin' - elif sys.platform.find('irix') != -1: - return 'irix' - elif sys.platform.find('sunos') != -1: - return 'sunos' - elif sys.platform.find('hp-ux') != -1: - return 'hpux' - elif sys.platform.find('aix') != -1: - return 'aix' - elif sys.platform.find('darwin') != -1: - return 'darwin' - else: - return 'posix' - elif os.name == 'os2': - return 'os2' - else: - return sys.platform - -def platform_module(name = platform_default()): - """Return the imported module for the platform. - - This looks for a module name that matches the specified argument. - If the name is unspecified, we fetch the appropriate default for - our execution environment. - """ - full_name = 'SCons.Platform.' + name - if full_name not in sys.modules: - if os.name == 'java': - eval(full_name) - else: - try: - file, path, desc = imp.find_module(name, - sys.modules['SCons.Platform'].__path__) - try: - mod = imp.load_module(full_name, file, path, desc) - finally: - if file: - file.close() - except ImportError: - try: - import zipimport - importer = zipimport.zipimporter( sys.modules['SCons.Platform'].__path__[0] ) - mod = importer.load_module(full_name) - except ImportError: - raise SCons.Errors.UserError("No platform named '%s'" % name) - setattr(SCons.Platform, name, mod) - return sys.modules[full_name] - -def DefaultToolList(platform, env): - """Select a default tool list for the specified platform. - """ - return SCons.Tool.tool_list(platform, env) - -class PlatformSpec(object): - def __init__(self, name, generate): - self.name = name - self.generate = generate - - def __call__(self, *args, **kw): - return self.generate(*args, **kw) - - def __str__(self): - return self.name - -class TempFileMunge(object): - """A callable class. You can set an Environment variable to this, - then call it with a string argument, then it will perform temporary - file substitution on it. This is used to circumvent the long command - line limitation. - - Example usage: - env["TEMPFILE"] = TempFileMunge - env["LINKCOM"] = "${TEMPFILE('$LINK $TARGET $SOURCES')}" - - By default, the name of the temporary file used begins with a - prefix of '@'. This may be configred for other tool chains by - setting '$TEMPFILEPREFIX'. - - env["TEMPFILEPREFIX"] = '-@' # diab compiler - env["TEMPFILEPREFIX"] = '-via' # arm tool chain - """ - def __init__(self, cmd): - self.cmd = cmd - - def __call__(self, target, source, env, for_signature): - if for_signature: - # If we're being called for signature calculation, it's - # because we're being called by the string expansion in - # Subst.py, which has the logic to strip any $( $) that - # may be in the command line we squirreled away. So we - # just return the raw command line and let the upper - # string substitution layers do their thing. - return self.cmd - - # Now we're actually being called because someone is actually - # going to try to execute the command, so we have to do our - # own expansion. - cmd = env.subst_list(self.cmd, SCons.Subst.SUBST_CMD, target, source)[0] - try: - maxline = int(env.subst('$MAXLINELENGTH')) - except ValueError: - maxline = 2048 - - length = 0 - for c in cmd: - length += len(c) - if length <= maxline: - return self.cmd - - # We do a normpath because mktemp() has what appears to be - # a bug in Windows that will use a forward slash as a path - # delimiter. Windows's link mistakes that for a command line - # switch and barfs. - # - # We use the .lnk suffix for the benefit of the Phar Lap - # linkloc linker, which likes to append an .lnk suffix if - # none is given. - (fd, tmp) = tempfile.mkstemp('.lnk', text=True) - native_tmp = SCons.Util.get_native_path(os.path.normpath(tmp)) - - if env['SHELL'] and env['SHELL'] == 'sh': - # The sh shell will try to escape the backslashes in the - # path, so unescape them. - native_tmp = native_tmp.replace('\\', r'\\\\') - # In Cygwin, we want to use rm to delete the temporary - # file, because del does not exist in the sh shell. - rm = env.Detect('rm') or 'del' - else: - # Don't use 'rm' if the shell is not sh, because rm won't - # work with the Windows shells (cmd.exe or command.com) or - # Windows path names. - rm = 'del' - - prefix = env.subst('$TEMPFILEPREFIX') - if not prefix: - prefix = '@' - - args = list(map(SCons.Subst.quote_spaces, cmd[1:])) - os.write(fd, " ".join(args) + "\n") - os.close(fd) - # XXX Using the SCons.Action.print_actions value directly - # like this is bogus, but expedient. This class should - # really be rewritten as an Action that defines the - # __call__() and strfunction() methods and lets the - # normal action-execution logic handle whether or not to - # print/execute the action. The problem, though, is all - # of that is decided before we execute this method as - # part of expanding the $TEMPFILE construction variable. - # Consequently, refactoring this will have to wait until - # we get more flexible with allowing Actions to exist - # independently and get strung together arbitrarily like - # Ant tasks. In the meantime, it's going to be more - # user-friendly to not let obsession with architectural - # purity get in the way of just being helpful, so we'll - # reach into SCons.Action directly. - if SCons.Action.print_actions: - print("Using tempfile "+native_tmp+" for command line:\n"+ - str(cmd[0]) + " " + " ".join(args)) - return [ cmd[0], prefix + native_tmp + '\n' + rm, native_tmp ] - -def Platform(name = platform_default()): - """Select a canned Platform specification. - """ - module = platform_module(name) - spec = PlatformSpec(name, module.generate) - return spec - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Platform/aix.py b/scons/scons-local-2.2.0/SCons/Platform/aix.py deleted file mode 100644 index df3ee4044..000000000 --- a/scons/scons-local-2.2.0/SCons/Platform/aix.py +++ /dev/null @@ -1,69 +0,0 @@ -"""engine.SCons.Platform.aix - -Platform-specific initialization for IBM AIX systems. - -There normally shouldn't be any need to import this module directly. It -will usually be imported through the generic SCons.Platform.Platform() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Platform/aix.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os - -import posix - -def get_xlc(env, xlc=None, xlc_r=None, packages=[]): - # Use the AIX package installer tool lslpp to figure out where a - # given xl* compiler is installed and what version it is. - xlcPath = None - xlcVersion = None - - if xlc is None: - xlc = env.get('CC', 'xlc') - if xlc_r is None: - xlc_r = xlc + '_r' - for package in packages: - cmd = "lslpp -fc " + package + " 2>/dev/null | egrep '" + xlc + "([^-_a-zA-Z0-9].*)?$'" - line = os.popen(cmd).readline() - if line: - v, p = line.split(':')[1:3] - xlcVersion = v.split()[1] - xlcPath = p.split()[0] - xlcPath = xlcPath[:xlcPath.rindex('/')] - break - return (xlcPath, xlc, xlc_r, xlcVersion) - -def generate(env): - posix.generate(env) - #Based on AIX 5.2: ARG_MAX=24576 - 3000 for environment expansion - env['MAXLINELENGTH'] = 21576 - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Platform/cygwin.py b/scons/scons-local-2.2.0/SCons/Platform/cygwin.py deleted file mode 100644 index 31d822b96..000000000 --- a/scons/scons-local-2.2.0/SCons/Platform/cygwin.py +++ /dev/null @@ -1,55 +0,0 @@ -"""SCons.Platform.cygwin - -Platform-specific initialization for Cygwin systems. - -There normally shouldn't be any need to import this module directly. It -will usually be imported through the generic SCons.Platform.Platform() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Platform/cygwin.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import posix -from SCons.Platform import TempFileMunge - -def generate(env): - posix.generate(env) - - env['PROGPREFIX'] = '' - env['PROGSUFFIX'] = '.exe' - env['SHLIBPREFIX'] = '' - env['SHLIBSUFFIX'] = '.dll' - env['LIBPREFIXES'] = [ '$LIBPREFIX', '$SHLIBPREFIX' ] - env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ] - env['TEMPFILE'] = TempFileMunge - env['TEMPFILEPREFIX'] = '@' - env['MAXLINELENGTH'] = 2048 - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Platform/darwin.py b/scons/scons-local-2.2.0/SCons/Platform/darwin.py deleted file mode 100644 index 907ec45d1..000000000 --- a/scons/scons-local-2.2.0/SCons/Platform/darwin.py +++ /dev/null @@ -1,70 +0,0 @@ -"""engine.SCons.Platform.darwin - -Platform-specific initialization for Mac OS X systems. - -There normally shouldn't be any need to import this module directly. It -will usually be imported through the generic SCons.Platform.Platform() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Platform/darwin.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import posix -import os - -def generate(env): - posix.generate(env) - env['SHLIBSUFFIX'] = '.dylib' - # put macports paths at front to override Apple's versions, fink path is after - # For now let people who want Macports or Fink tools specify it! - # env['ENV']['PATH'] = '/opt/local/bin:/opt/local/sbin:' + env['ENV']['PATH'] + ':/sw/bin' - - # Store extra system paths in env['ENV']['PATHOSX'] - - filelist = ['/etc/paths',] - # make sure this works on Macs with Tiger or earlier - try: - dirlist = os.listdir('/etc/paths.d') - except: - dirlist = [] - - for file in dirlist: - filelist.append('/etc/paths.d/'+file) - - for file in filelist: - if os.path.isfile(file): - f = open(file, 'r') - lines = f.readlines() - for line in lines: - if line: - env.AppendENVPath('PATHOSX', line.strip('\n')) - f.close() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Platform/hpux.py b/scons/scons-local-2.2.0/SCons/Platform/hpux.py deleted file mode 100644 index 2312439a8..000000000 --- a/scons/scons-local-2.2.0/SCons/Platform/hpux.py +++ /dev/null @@ -1,46 +0,0 @@ -"""engine.SCons.Platform.hpux - -Platform-specific initialization for HP-UX systems. - -There normally shouldn't be any need to import this module directly. It -will usually be imported through the generic SCons.Platform.Platform() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Platform/hpux.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import posix - -def generate(env): - posix.generate(env) - #Based on HP-UX11i: ARG_MAX=2048000 - 3000 for environment expansion - env['MAXLINELENGTH'] = 2045000 - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Platform/irix.py b/scons/scons-local-2.2.0/SCons/Platform/irix.py deleted file mode 100644 index f0b089a86..000000000 --- a/scons/scons-local-2.2.0/SCons/Platform/irix.py +++ /dev/null @@ -1,44 +0,0 @@ -"""SCons.Platform.irix - -Platform-specific initialization for SGI IRIX systems. - -There normally shouldn't be any need to import this module directly. It -will usually be imported through the generic SCons.Platform.Platform() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Platform/irix.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import posix - -def generate(env): - posix.generate(env) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Platform/os2.py b/scons/scons-local-2.2.0/SCons/Platform/os2.py deleted file mode 100644 index 0576abdd6..000000000 --- a/scons/scons-local-2.2.0/SCons/Platform/os2.py +++ /dev/null @@ -1,58 +0,0 @@ -"""SCons.Platform.os2 - -Platform-specific initialization for OS/2 systems. - -There normally shouldn't be any need to import this module directly. It -will usually be imported through the generic SCons.Platform.Platform() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Platform/os2.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" -import win32 - -def generate(env): - if 'ENV' not in env: - env['ENV'] = {} - env['OBJPREFIX'] = '' - env['OBJSUFFIX'] = '.obj' - env['SHOBJPREFIX'] = '$OBJPREFIX' - env['SHOBJSUFFIX'] = '$OBJSUFFIX' - env['PROGPREFIX'] = '' - env['PROGSUFFIX'] = '.exe' - env['LIBPREFIX'] = '' - env['LIBSUFFIX'] = '.lib' - env['SHLIBPREFIX'] = '' - env['SHLIBSUFFIX'] = '.dll' - env['LIBPREFIXES'] = '$LIBPREFIX' - env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ] - env['HOST_OS'] = 'os2' - env['HOST_ARCH'] = win32.get_architecture().arch - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Platform/posix.py b/scons/scons-local-2.2.0/SCons/Platform/posix.py deleted file mode 100644 index cff1f93a4..000000000 --- a/scons/scons-local-2.2.0/SCons/Platform/posix.py +++ /dev/null @@ -1,263 +0,0 @@ -"""SCons.Platform.posix - -Platform-specific initialization for POSIX (Linux, UNIX, etc.) systems. - -There normally shouldn't be any need to import this module directly. It -will usually be imported through the generic SCons.Platform.Platform() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Platform/posix.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import errno -import os -import os.path -import subprocess -import sys -import select - -import SCons.Util -from SCons.Platform import TempFileMunge - -exitvalmap = { - 2 : 127, - 13 : 126, -} - -def escape(arg): - "escape shell special characters" - slash = '\\' - special = '"$()' - - arg = arg.replace(slash, slash+slash) - for c in special: - arg = arg.replace(c, slash+c) - - return '"' + arg + '"' - -def exec_system(l, env): - stat = os.system(' '.join(l)) - if stat & 0xff: - return stat | 0x80 - return stat >> 8 - -def exec_spawnvpe(l, env): - stat = os.spawnvpe(os.P_WAIT, l[0], l, env) - # os.spawnvpe() returns the actual exit code, not the encoding - # returned by os.waitpid() or os.system(). - return stat - -def exec_fork(l, env): - pid = os.fork() - if not pid: - # Child process. - exitval = 127 - try: - os.execvpe(l[0], l, env) - except OSError, e: - exitval = exitvalmap.get(e[0], e[0]) - sys.stderr.write("scons: %s: %s\n" % (l[0], e[1])) - os._exit(exitval) - else: - # Parent process. - pid, stat = os.waitpid(pid, 0) - if stat & 0xff: - return stat | 0x80 - return stat >> 8 - -def _get_env_command(sh, escape, cmd, args, env): - s = ' '.join(args) - if env: - l = ['env', '-'] + \ - [escape(t[0])+'='+escape(t[1]) for t in env.items()] + \ - [sh, '-c', escape(s)] - s = ' '.join(l) - return s - -def env_spawn(sh, escape, cmd, args, env): - return exec_system([_get_env_command( sh, escape, cmd, args, env)], env) - -def spawnvpe_spawn(sh, escape, cmd, args, env): - return exec_spawnvpe([sh, '-c', ' '.join(args)], env) - -def fork_spawn(sh, escape, cmd, args, env): - return exec_fork([sh, '-c', ' '.join(args)], env) - -def process_cmd_output(cmd_stdout, cmd_stderr, stdout, stderr): - stdout_eof = stderr_eof = 0 - while not (stdout_eof and stderr_eof): - try: - (i,o,e) = select.select([cmd_stdout, cmd_stderr], [], []) - if cmd_stdout in i: - str = cmd_stdout.read() - if len(str) == 0: - stdout_eof = 1 - elif stdout is not None: - stdout.write(str) - if cmd_stderr in i: - str = cmd_stderr.read() - if len(str) == 0: - #sys.__stderr__.write( "stderr_eof=1\n" ) - stderr_eof = 1 - else: - #sys.__stderr__.write( "str(stderr) = %s\n" % str ) - stderr.write(str) - except select.error, (_errno, _strerror): - if _errno != errno.EINTR: - raise - -def exec_popen3(l, env, stdout, stderr): - proc = subprocess.Popen(' '.join(l), - stdout=stdout, - stderr=stderr, - shell=True) - stat = proc.wait() - if stat & 0xff: - return stat | 0x80 - return stat >> 8 - -def exec_piped_fork(l, env, stdout, stderr): - # spawn using fork / exec and providing a pipe for the command's - # stdout / stderr stream - if stdout != stderr: - (rFdOut, wFdOut) = os.pipe() - (rFdErr, wFdErr) = os.pipe() - else: - (rFdOut, wFdOut) = os.pipe() - rFdErr = rFdOut - wFdErr = wFdOut - # do the fork - pid = os.fork() - if not pid: - # Child process - os.close( rFdOut ) - if rFdOut != rFdErr: - os.close( rFdErr ) - os.dup2( wFdOut, 1 ) # is there some symbolic way to do that ? - os.dup2( wFdErr, 2 ) - os.close( wFdOut ) - if stdout != stderr: - os.close( wFdErr ) - exitval = 127 - try: - os.execvpe(l[0], l, env) - except OSError, e: - exitval = exitvalmap.get(e[0], e[0]) - stderr.write("scons: %s: %s\n" % (l[0], e[1])) - os._exit(exitval) - else: - # Parent process - pid, stat = os.waitpid(pid, 0) - os.close( wFdOut ) - if stdout != stderr: - os.close( wFdErr ) - childOut = os.fdopen( rFdOut ) - if stdout != stderr: - childErr = os.fdopen( rFdErr ) - else: - childErr = childOut - process_cmd_output(childOut, childErr, stdout, stderr) - os.close( rFdOut ) - if stdout != stderr: - os.close( rFdErr ) - if stat & 0xff: - return stat | 0x80 - return stat >> 8 - -def piped_env_spawn(sh, escape, cmd, args, env, stdout, stderr): - # spawn using Popen3 combined with the env command - # the command name and the command's stdout is written to stdout - # the command's stderr is written to stderr - return exec_popen3([_get_env_command(sh, escape, cmd, args, env)], - env, stdout, stderr) - -def piped_fork_spawn(sh, escape, cmd, args, env, stdout, stderr): - # spawn using fork / exec and providing a pipe for the command's - # stdout / stderr stream - return exec_piped_fork([sh, '-c', ' '.join(args)], - env, stdout, stderr) - - - -def generate(env): - # If os.spawnvpe() exists, we use it to spawn commands. Otherwise - # if the env utility exists, we use os.system() to spawn commands, - # finally we fall back on os.fork()/os.exec(). - # - # os.spawnvpe() is prefered because it is the most efficient. But - # for Python versions without it, os.system() is prefered because it - # is claimed that it works better with threads (i.e. -j) and is more - # efficient than forking Python. - # - # NB: Other people on the scons-users mailing list have claimed that - # os.fork()/os.exec() works better than os.system(). There may just - # not be a default that works best for all users. - - if 'spawnvpe' in os.__dict__: - spawn = spawnvpe_spawn - elif env.Detect('env'): - spawn = env_spawn - else: - spawn = fork_spawn - - if env.Detect('env'): - pspawn = piped_env_spawn - else: - pspawn = piped_fork_spawn - - if 'ENV' not in env: - env['ENV'] = {} - env['ENV']['PATH'] = '/usr/local/bin:/opt/bin:/bin:/usr/bin' - env['OBJPREFIX'] = '' - env['OBJSUFFIX'] = '.o' - env['SHOBJPREFIX'] = '$OBJPREFIX' - env['SHOBJSUFFIX'] = '$OBJSUFFIX' - env['PROGPREFIX'] = '' - env['PROGSUFFIX'] = '' - env['LIBPREFIX'] = 'lib' - env['LIBSUFFIX'] = '.a' - env['SHLIBPREFIX'] = '$LIBPREFIX' - env['SHLIBSUFFIX'] = '.so' - env['LIBPREFIXES'] = [ '$LIBPREFIX' ] - env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ] - env['PSPAWN'] = pspawn - env['SPAWN'] = spawn - env['SHELL'] = 'sh' - env['ESCAPE'] = escape - env['TEMPFILE'] = TempFileMunge - env['TEMPFILEPREFIX'] = '@' - #Based on LINUX: ARG_MAX=ARG_MAX=131072 - 3000 for environment expansion - #Note: specific platforms might rise or lower this value - env['MAXLINELENGTH'] = 128072 - - # This platform supports RPATH specifications. - env['__RPATH'] = '$_RPATH' - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Platform/sunos.py b/scons/scons-local-2.2.0/SCons/Platform/sunos.py deleted file mode 100644 index 7ae60f840..000000000 --- a/scons/scons-local-2.2.0/SCons/Platform/sunos.py +++ /dev/null @@ -1,50 +0,0 @@ -"""engine.SCons.Platform.sunos - -Platform-specific initialization for Sun systems. - -There normally shouldn't be any need to import this module directly. It -will usually be imported through the generic SCons.Platform.Platform() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Platform/sunos.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import posix - -def generate(env): - posix.generate(env) - # Based on sunSparc 8:32bit - # ARG_MAX=1048320 - 3000 for environment expansion - env['MAXLINELENGTH'] = 1045320 - env['PKGINFO'] = 'pkginfo' - env['PKGCHK'] = '/usr/sbin/pkgchk' - env['ENV']['PATH'] = env['ENV']['PATH'] + ':/opt/SUNWspro/bin:/usr/ccs/bin' - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Platform/win32.py b/scons/scons-local-2.2.0/SCons/Platform/win32.py deleted file mode 100644 index 2d6d970d3..000000000 --- a/scons/scons-local-2.2.0/SCons/Platform/win32.py +++ /dev/null @@ -1,384 +0,0 @@ -"""SCons.Platform.win32 - -Platform-specific initialization for Win32 systems. - -There normally shouldn't be any need to import this module directly. It -will usually be imported through the generic SCons.Platform.Platform() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Platform/win32.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os -import os.path -import sys -import tempfile - -from SCons.Platform.posix import exitvalmap -from SCons.Platform import TempFileMunge -import SCons.Util - -try: - import msvcrt - import win32api - import win32con - - msvcrt.get_osfhandle - win32api.SetHandleInformation - win32con.HANDLE_FLAG_INHERIT -except ImportError: - parallel_msg = \ - "you do not seem to have the pywin32 extensions installed;\n" + \ - "\tparallel (-j) builds may not work reliably with open Python files." -except AttributeError: - parallel_msg = \ - "your pywin32 extensions do not support file handle operations;\n" + \ - "\tparallel (-j) builds may not work reliably with open Python files." -else: - parallel_msg = None - - import builtins - - _builtin_file = builtins.file - _builtin_open = builtins.open - - class _scons_file(_builtin_file): - def __init__(self, *args, **kw): - _builtin_file.__init__(self, *args, **kw) - win32api.SetHandleInformation(msvcrt.get_osfhandle(self.fileno()), - win32con.HANDLE_FLAG_INHERIT, 0) - - def _scons_open(*args, **kw): - fp = _builtin_open(*args, **kw) - win32api.SetHandleInformation(msvcrt.get_osfhandle(fp.fileno()), - win32con.HANDLE_FLAG_INHERIT, - 0) - return fp - - builtins.file = _scons_file - builtins.open = _scons_open - - - -# The upshot of all this is that, if you are using Python 1.5.2, -# you had better have cmd or command.com in your PATH when you run -# scons. - -def piped_spawn(sh, escape, cmd, args, env, stdout, stderr): - # There is no direct way to do that in python. What we do - # here should work for most cases: - # In case stdout (stderr) is not redirected to a file, - # we redirect it into a temporary file tmpFileStdout - # (tmpFileStderr) and copy the contents of this file - # to stdout (stderr) given in the argument - if not sh: - sys.stderr.write("scons: Could not find command interpreter, is it in your PATH?\n") - return 127 - else: - # one temporary file for stdout and stderr - tmpFileStdout = os.path.normpath(tempfile.mktemp()) - tmpFileStderr = os.path.normpath(tempfile.mktemp()) - - # check if output is redirected - stdoutRedirected = 0 - stderrRedirected = 0 - for arg in args: - # are there more possibilities to redirect stdout ? - if (arg.find( ">", 0, 1 ) != -1 or - arg.find( "1>", 0, 2 ) != -1): - stdoutRedirected = 1 - # are there more possibilities to redirect stderr ? - if arg.find( "2>", 0, 2 ) != -1: - stderrRedirected = 1 - - # redirect output of non-redirected streams to our tempfiles - if stdoutRedirected == 0: - args.append(">" + str(tmpFileStdout)) - if stderrRedirected == 0: - args.append("2>" + str(tmpFileStderr)) - - # actually do the spawn - try: - args = [sh, '/C', escape(' '.join(args)) ] - ret = os.spawnve(os.P_WAIT, sh, args, env) - except OSError, e: - # catch any error - try: - ret = exitvalmap[e[0]] - except KeyError: - sys.stderr.write("scons: unknown OSError exception code %d - %s: %s\n" % (e[0], cmd, e[1])) - if stderr is not None: - stderr.write("scons: %s: %s\n" % (cmd, e[1])) - # copy child output from tempfiles to our streams - # and do clean up stuff - if stdout is not None and stdoutRedirected == 0: - try: - stdout.write(open( tmpFileStdout, "r" ).read()) - os.remove( tmpFileStdout ) - except (IOError, OSError): - pass - - if stderr is not None and stderrRedirected == 0: - try: - stderr.write(open( tmpFileStderr, "r" ).read()) - os.remove( tmpFileStderr ) - except (IOError, OSError): - pass - return ret - -def exec_spawn(l, env): - try: - result = os.spawnve(os.P_WAIT, l[0], l, env) - except OSError, e: - try: - result = exitvalmap[e[0]] - sys.stderr.write("scons: %s: %s\n" % (l[0], e[1])) - except KeyError: - result = 127 - if len(l) > 2: - if len(l[2]) < 1000: - command = ' '.join(l[0:3]) - else: - command = l[0] - else: - command = l[0] - sys.stderr.write("scons: unknown OSError exception code %d - '%s': %s\n" % (e[0], command, e[1])) - return result - -def spawn(sh, escape, cmd, args, env): - if not sh: - sys.stderr.write("scons: Could not find command interpreter, is it in your PATH?\n") - return 127 - return exec_spawn([sh, '/C', escape(' '.join(args))], env) - -# Windows does not allow special characters in file names anyway, so no -# need for a complex escape function, we will just quote the arg, except -# that "cmd /c" requires that if an argument ends with a backslash it -# needs to be escaped so as not to interfere with closing double quote -# that we add. -def escape(x): - if x[-1] == '\\': - x = x + '\\' - return '"' + x + '"' - -# Get the windows system directory name -_system_root = None - -def get_system_root(): - global _system_root - if _system_root is not None: - return _system_root - - # A resonable default if we can't read the registry - val = os.environ.get('SystemRoot', "C:\\WINDOWS") - - if SCons.Util.can_read_reg: - try: - # Look for Windows NT system root - k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, - 'Software\\Microsoft\\Windows NT\\CurrentVersion') - val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot') - except SCons.Util.RegError: - try: - # Okay, try the Windows 9x system root - k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, - 'Software\\Microsoft\\Windows\\CurrentVersion') - val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot') - except KeyboardInterrupt: - raise - except: - pass - _system_root = val - return val - -# Get the location of the program files directory -def get_program_files_dir(): - # Now see if we can look in the registry... - val = '' - if SCons.Util.can_read_reg: - try: - # Look for Windows Program Files directory - k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, - 'Software\\Microsoft\\Windows\\CurrentVersion') - val, tok = SCons.Util.RegQueryValueEx(k, 'ProgramFilesDir') - except SCons.Util.RegError: - val = '' - pass - - if val == '': - # A reasonable default if we can't read the registry - # (Actually, it's pretty reasonable even if we can :-) - val = os.path.join(os.path.dirname(get_system_root()),"Program Files") - - return val - - - -# Determine which windows CPU were running on. -class ArchDefinition(object): - """ - A class for defining architecture-specific settings and logic. - """ - def __init__(self, arch, synonyms=[]): - self.arch = arch - self.synonyms = synonyms - -SupportedArchitectureList = [ - ArchDefinition( - 'x86', - ['i386', 'i486', 'i586', 'i686'], - ), - - ArchDefinition( - 'x86_64', - ['AMD64', 'amd64', 'em64t', 'EM64T', 'x86_64'], - ), - - ArchDefinition( - 'ia64', - ['IA64'], - ), -] - -SupportedArchitectureMap = {} -for a in SupportedArchitectureList: - SupportedArchitectureMap[a.arch] = a - for s in a.synonyms: - SupportedArchitectureMap[s] = a - -def get_architecture(arch=None): - """Returns the definition for the specified architecture string. - - If no string is specified, the system default is returned (as defined - by the PROCESSOR_ARCHITEW6432 or PROCESSOR_ARCHITECTURE environment - variables). - """ - if arch is None: - arch = os.environ.get('PROCESSOR_ARCHITEW6432') - if not arch: - arch = os.environ.get('PROCESSOR_ARCHITECTURE') - return SupportedArchitectureMap.get(arch, ArchDefinition('', [''])) - -def generate(env): - # Attempt to find cmd.exe (for WinNT/2k/XP) or - # command.com for Win9x - cmd_interp = '' - # First see if we can look in the registry... - if SCons.Util.can_read_reg: - try: - # Look for Windows NT system root - k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, - 'Software\\Microsoft\\Windows NT\\CurrentVersion') - val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot') - cmd_interp = os.path.join(val, 'System32\\cmd.exe') - except SCons.Util.RegError: - try: - # Okay, try the Windows 9x system root - k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, - 'Software\\Microsoft\\Windows\\CurrentVersion') - val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot') - cmd_interp = os.path.join(val, 'command.com') - except KeyboardInterrupt: - raise - except: - pass - - # For the special case of not having access to the registry, we - # use a temporary path and pathext to attempt to find the command - # interpreter. If we fail, we try to find the interpreter through - # the env's PATH. The problem with that is that it might not - # contain an ENV and a PATH. - if not cmd_interp: - systemroot = get_system_root() - tmp_path = systemroot + os.pathsep + \ - os.path.join(systemroot,'System32') - tmp_pathext = '.com;.exe;.bat;.cmd' - if 'PATHEXT' in os.environ: - tmp_pathext = os.environ['PATHEXT'] - cmd_interp = SCons.Util.WhereIs('cmd', tmp_path, tmp_pathext) - if not cmd_interp: - cmd_interp = SCons.Util.WhereIs('command', tmp_path, tmp_pathext) - - if not cmd_interp: - cmd_interp = env.Detect('cmd') - if not cmd_interp: - cmd_interp = env.Detect('command') - - - if 'ENV' not in env: - env['ENV'] = {} - - # Import things from the external environment to the construction - # environment's ENV. This is a potential slippery slope, because we - # *don't* want to make builds dependent on the user's environment by - # default. We're doing this for SystemRoot, though, because it's - # needed for anything that uses sockets, and seldom changes, and - # for SystemDrive because it's related. - # - # Weigh the impact carefully before adding other variables to this list. - import_env = [ 'SystemDrive', 'SystemRoot', 'TEMP', 'TMP' ] - for var in import_env: - v = os.environ.get(var) - if v: - env['ENV'][var] = v - - if 'COMSPEC' not in env['ENV']: - v = os.environ.get("COMSPEC") - if v: - env['ENV']['COMSPEC'] = v - - env.AppendENVPath('PATH', get_system_root() + '\System32') - - env['ENV']['PATHEXT'] = '.COM;.EXE;.BAT;.CMD' - env['OBJPREFIX'] = '' - env['OBJSUFFIX'] = '.obj' - env['SHOBJPREFIX'] = '$OBJPREFIX' - env['SHOBJSUFFIX'] = '$OBJSUFFIX' - env['PROGPREFIX'] = '' - env['PROGSUFFIX'] = '.exe' - env['LIBPREFIX'] = '' - env['LIBSUFFIX'] = '.lib' - env['SHLIBPREFIX'] = '' - env['SHLIBSUFFIX'] = '.dll' - env['LIBPREFIXES'] = [ '$LIBPREFIX' ] - env['LIBSUFFIXES'] = [ '$LIBSUFFIX' ] - env['PSPAWN'] = piped_spawn - env['SPAWN'] = spawn - env['SHELL'] = cmd_interp - env['TEMPFILE'] = TempFileMunge - env['TEMPFILEPREFIX'] = '@' - env['MAXLINELENGTH'] = 2048 - env['ESCAPE'] = escape - - env['HOST_OS'] = 'win32' - env['HOST_ARCH'] = get_architecture().arch - - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/SConf.py b/scons/scons-local-2.2.0/SCons/SConf.py deleted file mode 100644 index 5c99db9d3..000000000 --- a/scons/scons-local-2.2.0/SCons/SConf.py +++ /dev/null @@ -1,1030 +0,0 @@ -"""SCons.SConf - -Autoconf-like configuration support. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/SConf.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.compat - -import io -import os -import re -import sys -import traceback - -import SCons.Action -import SCons.Builder -import SCons.Errors -import SCons.Job -import SCons.Node.FS -import SCons.Taskmaster -import SCons.Util -import SCons.Warnings -import SCons.Conftest - -from SCons.Debug import Trace - -# Turn off the Conftest error logging -SCons.Conftest.LogInputFiles = 0 -SCons.Conftest.LogErrorMessages = 0 - -# Set -build_type = None -build_types = ['clean', 'help'] - -def SetBuildType(type): - global build_type - build_type = type - -# to be set, if we are in dry-run mode -dryrun = 0 - -AUTO=0 # use SCons dependency scanning for up-to-date checks -FORCE=1 # force all tests to be rebuilt -CACHE=2 # force all tests to be taken from cache (raise an error, if necessary) -cache_mode = AUTO - -def SetCacheMode(mode): - """Set the Configure cache mode. mode must be one of "auto", "force", - or "cache".""" - global cache_mode - if mode == "auto": - cache_mode = AUTO - elif mode == "force": - cache_mode = FORCE - elif mode == "cache": - cache_mode = CACHE - else: - raise ValueError("SCons.SConf.SetCacheMode: Unknown mode " + mode) - -progress_display = SCons.Util.display # will be overwritten by SCons.Script -def SetProgressDisplay(display): - """Set the progress display to use (called from SCons.Script)""" - global progress_display - progress_display = display - -SConfFS = None - -_ac_build_counter = 0 # incremented, whenever TryBuild is called -_ac_config_logs = {} # all config.log files created in this build -_ac_config_hs = {} # all config.h files created in this build -sconf_global = None # current sconf object - -def _createConfigH(target, source, env): - t = open(str(target[0]), "w") - defname = re.sub('[^A-Za-z0-9_]', '_', str(target[0]).upper()) - t.write("""#ifndef %(DEFNAME)s_SEEN -#define %(DEFNAME)s_SEEN - -""" % {'DEFNAME' : defname}) - t.write(source[0].get_contents()) - t.write(""" -#endif /* %(DEFNAME)s_SEEN */ -""" % {'DEFNAME' : defname}) - t.close() - -def _stringConfigH(target, source, env): - return "scons: Configure: creating " + str(target[0]) - -def CreateConfigHBuilder(env): - """Called just before the building targets phase begins.""" - if len(_ac_config_hs) == 0: - return - action = SCons.Action.Action(_createConfigH, - _stringConfigH) - sconfigHBld = SCons.Builder.Builder(action=action) - env.Append( BUILDERS={'SConfigHBuilder':sconfigHBld} ) - for k in _ac_config_hs.keys(): - env.SConfigHBuilder(k, env.Value(_ac_config_hs[k])) - -class SConfWarning(SCons.Warnings.Warning): - pass -SCons.Warnings.enableWarningClass(SConfWarning) - -# some error definitions -class SConfError(SCons.Errors.UserError): - def __init__(self,msg): - SCons.Errors.UserError.__init__(self,msg) - -class ConfigureDryRunError(SConfError): - """Raised when a file or directory needs to be updated during a Configure - process, but the user requested a dry-run""" - def __init__(self,target): - if not isinstance(target, SCons.Node.FS.File): - msg = 'Cannot create configure directory "%s" within a dry-run.' % str(target) - else: - msg = 'Cannot update configure test "%s" within a dry-run.' % str(target) - SConfError.__init__(self,msg) - -class ConfigureCacheError(SConfError): - """Raised when a use explicitely requested the cache feature, but the test - is run the first time.""" - def __init__(self,target): - SConfError.__init__(self, '"%s" is not yet built and cache is forced.' % str(target)) - -# define actions for building text files -def _createSource( target, source, env ): - fd = open(str(target[0]), "w") - fd.write(source[0].get_contents()) - fd.close() -def _stringSource( target, source, env ): - return (str(target[0]) + ' <-\n |' + - source[0].get_contents().replace( '\n', "\n |" ) ) - -class SConfBuildInfo(SCons.Node.FS.FileBuildInfo): - """ - Special build info for targets of configure tests. Additional members - are result (did the builder succeed last time?) and string, which - contains messages of the original build phase. - """ - result = None # -> 0/None -> no error, != 0 error - string = None # the stdout / stderr output when building the target - - def set_build_result(self, result, string): - self.result = result - self.string = string - - -class Streamer(object): - """ - 'Sniffer' for a file-like writable object. Similar to the unix tool tee. - """ - def __init__(self, orig): - self.orig = orig - self.s = io.StringIO() - - def write(self, str): - if self.orig: - self.orig.write(str) - self.s.write(str) - - def writelines(self, lines): - for l in lines: - self.write(l + '\n') - - def getvalue(self): - """ - Return everything written to orig since the Streamer was created. - """ - return self.s.getvalue() - - def flush(self): - if self.orig: - self.orig.flush() - self.s.flush() - - -class SConfBuildTask(SCons.Taskmaster.AlwaysTask): - """ - This is almost the same as SCons.Script.BuildTask. Handles SConfErrors - correctly and knows about the current cache_mode. - """ - def display(self, message): - if sconf_global.logstream: - sconf_global.logstream.write("scons: Configure: " + message + "\n") - - def display_cached_string(self, bi): - """ - Logs the original builder messages, given the SConfBuildInfo instance - bi. - """ - if not isinstance(bi, SConfBuildInfo): - SCons.Warnings.warn(SConfWarning, - "The stored build information has an unexpected class: %s" % bi.__class__) - else: - self.display("The original builder output was:\n" + - (" |" + str(bi.string)).replace("\n", "\n |")) - - def failed(self): - # check, if the reason was a ConfigureDryRunError or a - # ConfigureCacheError and if yes, reraise the exception - exc_type = self.exc_info()[0] - if issubclass(exc_type, SConfError): - raise - elif issubclass(exc_type, SCons.Errors.BuildError): - # we ignore Build Errors (occurs, when a test doesn't pass) - # Clear the exception to prevent the contained traceback - # to build a reference cycle. - self.exc_clear() - else: - self.display('Caught exception while building "%s":\n' % - self.targets[0]) - try: - excepthook = sys.excepthook - except AttributeError: - # Earlier versions of Python don't have sys.excepthook... - def excepthook(type, value, tb): - traceback.print_tb(tb) - print type, value - excepthook(*self.exc_info()) - return SCons.Taskmaster.Task.failed(self) - - def collect_node_states(self): - # returns (is_up_to_date, cached_error, cachable) - # where is_up_to_date is 1, if the node(s) are up_to_date - # cached_error is 1, if the node(s) are up_to_date, but the - # build will fail - # cachable is 0, if some nodes are not in our cache - T = 0 - changed = False - cached_error = False - cachable = True - for t in self.targets: - if T: Trace('%s' % (t)) - bi = t.get_stored_info().binfo - if isinstance(bi, SConfBuildInfo): - if T: Trace(': SConfBuildInfo') - if cache_mode == CACHE: - t.set_state(SCons.Node.up_to_date) - if T: Trace(': set_state(up_to-date)') - else: - if T: Trace(': get_state() %s' % t.get_state()) - if T: Trace(': changed() %s' % t.changed()) - if (t.get_state() != SCons.Node.up_to_date and t.changed()): - changed = True - if T: Trace(': changed %s' % changed) - cached_error = cached_error or bi.result - else: - if T: Trace(': else') - # the node hasn't been built in a SConf context or doesn't - # exist - cachable = False - changed = ( t.get_state() != SCons.Node.up_to_date ) - if T: Trace(': changed %s' % changed) - if T: Trace('\n') - return (not changed, cached_error, cachable) - - def execute(self): - if not self.targets[0].has_builder(): - return - - sconf = sconf_global - - is_up_to_date, cached_error, cachable = self.collect_node_states() - - if cache_mode == CACHE and not cachable: - raise ConfigureCacheError(self.targets[0]) - elif cache_mode == FORCE: - is_up_to_date = 0 - - if cached_error and is_up_to_date: - self.display("Building \"%s\" failed in a previous run and all " - "its sources are up to date." % str(self.targets[0])) - binfo = self.targets[0].get_stored_info().binfo - self.display_cached_string(binfo) - raise SCons.Errors.BuildError # will be 'caught' in self.failed - elif is_up_to_date: - self.display("\"%s\" is up to date." % str(self.targets[0])) - binfo = self.targets[0].get_stored_info().binfo - self.display_cached_string(binfo) - elif dryrun: - raise ConfigureDryRunError(self.targets[0]) - else: - # note stdout and stderr are the same here - s = sys.stdout = sys.stderr = Streamer(sys.stdout) - try: - env = self.targets[0].get_build_env() - if cache_mode == FORCE: - # Set up the Decider() to force rebuilds by saying - # that every source has changed. Note that we still - # call the environment's underlying source decider so - # that the correct .sconsign info will get calculated - # and keep the build state consistent. - def force_build(dependency, target, prev_ni, - env_decider=env.decide_source): - env_decider(dependency, target, prev_ni) - return True - if env.decide_source.func_code is not force_build.func_code: - env.Decider(force_build) - env['PSTDOUT'] = env['PSTDERR'] = s - try: - sconf.cached = 0 - self.targets[0].build() - finally: - sys.stdout = sys.stderr = env['PSTDOUT'] = \ - env['PSTDERR'] = sconf.logstream - except KeyboardInterrupt: - raise - except SystemExit: - exc_value = sys.exc_info()[1] - raise SCons.Errors.ExplicitExit(self.targets[0],exc_value.code) - except Exception, e: - for t in self.targets: - binfo = t.get_binfo() - binfo.__class__ = SConfBuildInfo - binfo.set_build_result(1, s.getvalue()) - sconsign_entry = SCons.SConsign.SConsignEntry() - sconsign_entry.binfo = binfo - #sconsign_entry.ninfo = self.get_ninfo() - # We'd like to do this as follows: - # t.store_info(binfo) - # However, we need to store it as an SConfBuildInfo - # object, and store_info() will turn it into a - # regular FileNodeInfo if the target is itself a - # regular File. - sconsign = t.dir.sconsign() - sconsign.set_entry(t.name, sconsign_entry) - sconsign.merge() - raise e - else: - for t in self.targets: - binfo = t.get_binfo() - binfo.__class__ = SConfBuildInfo - binfo.set_build_result(0, s.getvalue()) - sconsign_entry = SCons.SConsign.SConsignEntry() - sconsign_entry.binfo = binfo - #sconsign_entry.ninfo = self.get_ninfo() - # We'd like to do this as follows: - # t.store_info(binfo) - # However, we need to store it as an SConfBuildInfo - # object, and store_info() will turn it into a - # regular FileNodeInfo if the target is itself a - # regular File. - sconsign = t.dir.sconsign() - sconsign.set_entry(t.name, sconsign_entry) - sconsign.merge() - -class SConfBase(object): - """This is simply a class to represent a configure context. After - creating a SConf object, you can call any tests. After finished with your - tests, be sure to call the Finish() method, which returns the modified - environment. - Some words about caching: In most cases, it is not necessary to cache - Test results explicitely. Instead, we use the scons dependency checking - mechanism. For example, if one wants to compile a test program - (SConf.TryLink), the compiler is only called, if the program dependencies - have changed. However, if the program could not be compiled in a former - SConf run, we need to explicitely cache this error. - """ - - def __init__(self, env, custom_tests = {}, conf_dir='$CONFIGUREDIR', - log_file='$CONFIGURELOG', config_h = None, _depth = 0): - """Constructor. Pass additional tests in the custom_tests-dictinary, - e.g. custom_tests={'CheckPrivate':MyPrivateTest}, where MyPrivateTest - defines a custom test. - Note also the conf_dir and log_file arguments (you may want to - build tests in the VariantDir, not in the SourceDir) - """ - global SConfFS - if not SConfFS: - SConfFS = SCons.Node.FS.default_fs or \ - SCons.Node.FS.FS(env.fs.pathTop) - if sconf_global is not None: - raise SCons.Errors.UserError - self.env = env - if log_file is not None: - log_file = SConfFS.File(env.subst(log_file)) - self.logfile = log_file - self.logstream = None - self.lastTarget = None - self.depth = _depth - self.cached = 0 # will be set, if all test results are cached - - # add default tests - default_tests = { - 'CheckCC' : CheckCC, - 'CheckCXX' : CheckCXX, - 'CheckSHCC' : CheckSHCC, - 'CheckSHCXX' : CheckSHCXX, - 'CheckFunc' : CheckFunc, - 'CheckType' : CheckType, - 'CheckTypeSize' : CheckTypeSize, - 'CheckDeclaration' : CheckDeclaration, - 'CheckHeader' : CheckHeader, - 'CheckCHeader' : CheckCHeader, - 'CheckCXXHeader' : CheckCXXHeader, - 'CheckLib' : CheckLib, - 'CheckLibWithHeader' : CheckLibWithHeader, - } - self.AddTests(default_tests) - self.AddTests(custom_tests) - self.confdir = SConfFS.Dir(env.subst(conf_dir)) - if config_h is not None: - config_h = SConfFS.File(config_h) - self.config_h = config_h - self._startup() - - def Finish(self): - """Call this method after finished with your tests: - env = sconf.Finish() - """ - self._shutdown() - return self.env - - def Define(self, name, value = None, comment = None): - """ - Define a pre processor symbol name, with the optional given value in the - current config header. - - If value is None (default), then #define name is written. If value is not - none, then #define name value is written. - - comment is a string which will be put as a C comment in the - header, to explain the meaning of the value (appropriate C comments /* and - */ will be put automatically.""" - lines = [] - if comment: - comment_str = "/* %s */" % comment - lines.append(comment_str) - - if value is not None: - define_str = "#define %s %s" % (name, value) - else: - define_str = "#define %s" % name - lines.append(define_str) - lines.append('') - - self.config_h_text = self.config_h_text + '\n'.join(lines) - - def BuildNodes(self, nodes): - """ - Tries to build the given nodes immediately. Returns 1 on success, - 0 on error. - """ - if self.logstream is not None: - # override stdout / stderr to write in log file - oldStdout = sys.stdout - sys.stdout = self.logstream - oldStderr = sys.stderr - sys.stderr = self.logstream - - # the engine assumes the current path is the SConstruct directory ... - old_fs_dir = SConfFS.getcwd() - old_os_dir = os.getcwd() - SConfFS.chdir(SConfFS.Top, change_os_dir=1) - - # Because we take responsibility here for writing out our - # own .sconsign info (see SConfBuildTask.execute(), above), - # we override the store_info() method with a null place-holder - # so we really control how it gets written. - for n in nodes: - n.store_info = n.do_not_store_info - - ret = 1 - - try: - # ToDo: use user options for calc - save_max_drift = SConfFS.get_max_drift() - SConfFS.set_max_drift(0) - tm = SCons.Taskmaster.Taskmaster(nodes, SConfBuildTask) - # we don't want to build tests in parallel - jobs = SCons.Job.Jobs(1, tm ) - jobs.run() - for n in nodes: - state = n.get_state() - if (state != SCons.Node.executed and - state != SCons.Node.up_to_date): - # the node could not be built. we return 0 in this case - ret = 0 - finally: - SConfFS.set_max_drift(save_max_drift) - os.chdir(old_os_dir) - SConfFS.chdir(old_fs_dir, change_os_dir=0) - if self.logstream is not None: - # restore stdout / stderr - sys.stdout = oldStdout - sys.stderr = oldStderr - return ret - - def pspawn_wrapper(self, sh, escape, cmd, args, env): - """Wrapper function for handling piped spawns. - - This looks to the calling interface (in Action.py) like a "normal" - spawn, but associates the call with the PSPAWN variable from - the construction environment and with the streams to which we - want the output logged. This gets slid into the construction - environment as the SPAWN variable so Action.py doesn't have to - know or care whether it's spawning a piped command or not. - """ - return self.pspawn(sh, escape, cmd, args, env, self.logstream, self.logstream) - - - def TryBuild(self, builder, text = None, extension = ""): - """Low level TryBuild implementation. Normally you don't need to - call that - you can use TryCompile / TryLink / TryRun instead - """ - global _ac_build_counter - - # Make sure we have a PSPAWN value, and save the current - # SPAWN value. - try: - self.pspawn = self.env['PSPAWN'] - except KeyError: - raise SCons.Errors.UserError('Missing PSPAWN construction variable.') - try: - save_spawn = self.env['SPAWN'] - except KeyError: - raise SCons.Errors.UserError('Missing SPAWN construction variable.') - - nodesToBeBuilt = [] - - f = "conftest_" + str(_ac_build_counter) - pref = self.env.subst( builder.builder.prefix ) - suff = self.env.subst( builder.builder.suffix ) - target = self.confdir.File(pref + f + suff) - - try: - # Slide our wrapper into the construction environment as - # the SPAWN function. - self.env['SPAWN'] = self.pspawn_wrapper - sourcetext = self.env.Value(text) - - if text is not None: - textFile = self.confdir.File(f + extension) - textFileNode = self.env.SConfSourceBuilder(target=textFile, - source=sourcetext) - nodesToBeBuilt.extend(textFileNode) - source = textFileNode - else: - source = None - - nodes = builder(target = target, source = source) - if not SCons.Util.is_List(nodes): - nodes = [nodes] - nodesToBeBuilt.extend(nodes) - result = self.BuildNodes(nodesToBeBuilt) - - finally: - self.env['SPAWN'] = save_spawn - - _ac_build_counter = _ac_build_counter + 1 - if result: - self.lastTarget = nodes[0] - else: - self.lastTarget = None - - return result - - def TryAction(self, action, text = None, extension = ""): - """Tries to execute the given action with optional source file - contents and optional source file extension , - Returns the status (0 : failed, 1 : ok) and the contents of the - output file. - """ - builder = SCons.Builder.Builder(action=action) - self.env.Append( BUILDERS = {'SConfActionBuilder' : builder} ) - ok = self.TryBuild(self.env.SConfActionBuilder, text, extension) - del self.env['BUILDERS']['SConfActionBuilder'] - if ok: - outputStr = self.lastTarget.get_contents() - return (1, outputStr) - return (0, "") - - def TryCompile( self, text, extension): - """Compiles the program given in text to an env.Object, using extension - as file extension (e.g. '.c'). Returns 1, if compilation was - successful, 0 otherwise. The target is saved in self.lastTarget (for - further processing). - """ - return self.TryBuild(self.env.Object, text, extension) - - def TryLink( self, text, extension ): - """Compiles the program given in text to an executable env.Program, - using extension as file extension (e.g. '.c'). Returns 1, if - compilation was successful, 0 otherwise. The target is saved in - self.lastTarget (for further processing). - """ - return self.TryBuild(self.env.Program, text, extension ) - - def TryRun(self, text, extension ): - """Compiles and runs the program given in text, using extension - as file extension (e.g. '.c'). Returns (1, outputStr) on success, - (0, '') otherwise. The target (a file containing the program's stdout) - is saved in self.lastTarget (for further processing). - """ - ok = self.TryLink(text, extension) - if( ok ): - prog = self.lastTarget - pname = prog.path - output = self.confdir.File(os.path.basename(pname)+'.out') - node = self.env.Command(output, prog, [ [ pname, ">", "${TARGET}"] ]) - ok = self.BuildNodes(node) - if ok: - outputStr = output.get_contents() - return( 1, outputStr) - return (0, "") - - class TestWrapper(object): - """A wrapper around Tests (to ensure sanity)""" - def __init__(self, test, sconf): - self.test = test - self.sconf = sconf - def __call__(self, *args, **kw): - if not self.sconf.active: - raise SCons.Errors.UserError - context = CheckContext(self.sconf) - ret = self.test(context, *args, **kw) - if self.sconf.config_h is not None: - self.sconf.config_h_text = self.sconf.config_h_text + context.config_h - context.Result("error: no result") - return ret - - def AddTest(self, test_name, test_instance): - """Adds test_class to this SConf instance. It can be called with - self.test_name(...)""" - setattr(self, test_name, SConfBase.TestWrapper(test_instance, self)) - - def AddTests(self, tests): - """Adds all the tests given in the tests dictionary to this SConf - instance - """ - for name in tests.keys(): - self.AddTest(name, tests[name]) - - def _createDir( self, node ): - dirName = str(node) - if dryrun: - if not os.path.isdir( dirName ): - raise ConfigureDryRunError(dirName) - else: - if not os.path.isdir( dirName ): - os.makedirs( dirName ) - node._exists = 1 - - def _startup(self): - """Private method. Set up logstream, and set the environment - variables necessary for a piped build - """ - global _ac_config_logs - global sconf_global - global SConfFS - - self.lastEnvFs = self.env.fs - self.env.fs = SConfFS - self._createDir(self.confdir) - self.confdir.up().add_ignore( [self.confdir] ) - - if self.logfile is not None and not dryrun: - # truncate logfile, if SConf.Configure is called for the first time - # in a build - if self.logfile in _ac_config_logs: - log_mode = "a" - else: - _ac_config_logs[self.logfile] = None - log_mode = "w" - fp = open(str(self.logfile), log_mode) - self.logstream = SCons.Util.Unbuffered(fp) - # logfile may stay in a build directory, so we tell - # the build system not to override it with a eventually - # existing file with the same name in the source directory - self.logfile.dir.add_ignore( [self.logfile] ) - - tb = traceback.extract_stack()[-3-self.depth] - old_fs_dir = SConfFS.getcwd() - SConfFS.chdir(SConfFS.Top, change_os_dir=0) - self.logstream.write('file %s,line %d:\n\tConfigure(confdir = %s)\n' % - (tb[0], tb[1], str(self.confdir)) ) - SConfFS.chdir(old_fs_dir) - else: - self.logstream = None - # we use a special builder to create source files from TEXT - action = SCons.Action.Action(_createSource, - _stringSource) - sconfSrcBld = SCons.Builder.Builder(action=action) - self.env.Append( BUILDERS={'SConfSourceBuilder':sconfSrcBld} ) - self.config_h_text = _ac_config_hs.get(self.config_h, "") - self.active = 1 - # only one SConf instance should be active at a time ... - sconf_global = self - - def _shutdown(self): - """Private method. Reset to non-piped spawn""" - global sconf_global, _ac_config_hs - - if not self.active: - raise SCons.Errors.UserError("Finish may be called only once!") - if self.logstream is not None and not dryrun: - self.logstream.write("\n") - self.logstream.close() - self.logstream = None - # remove the SConfSourceBuilder from the environment - blds = self.env['BUILDERS'] - del blds['SConfSourceBuilder'] - self.env.Replace( BUILDERS=blds ) - self.active = 0 - sconf_global = None - if not self.config_h is None: - _ac_config_hs[self.config_h] = self.config_h_text - self.env.fs = self.lastEnvFs - -class CheckContext(object): - """Provides a context for configure tests. Defines how a test writes to the - screen and log file. - - A typical test is just a callable with an instance of CheckContext as - first argument: - - def CheckCustom(context, ...) - context.Message('Checking my weird test ... ') - ret = myWeirdTestFunction(...) - context.Result(ret) - - Often, myWeirdTestFunction will be one of - context.TryCompile/context.TryLink/context.TryRun. The results of - those are cached, for they are only rebuild, if the dependencies have - changed. - """ - - def __init__(self, sconf): - """Constructor. Pass the corresponding SConf instance.""" - self.sconf = sconf - self.did_show_result = 0 - - # for Conftest.py: - self.vardict = {} - self.havedict = {} - self.headerfilename = None - self.config_h = "" # config_h text will be stored here - # we don't regenerate the config.h file after each test. That means, - # that tests won't be able to include the config.h file, and so - # they can't do an #ifdef HAVE_XXX_H. This shouldn't be a major - # issue, though. If it turns out, that we need to include config.h - # in tests, we must ensure, that the dependencies are worked out - # correctly. Note that we can't use Conftest.py's support for config.h, - # cause we will need to specify a builder for the config.h file ... - - def Message(self, text): - """Inform about what we are doing right now, e.g. - 'Checking for SOMETHING ... ' - """ - self.Display(text) - self.sconf.cached = 1 - self.did_show_result = 0 - - def Result(self, res): - """Inform about the result of the test. res may be an integer or a - string. In case of an integer, the written text will be 'yes' or 'no'. - The result is only displayed when self.did_show_result is not set. - """ - if isinstance(res, (int, bool)): - if res: - text = "yes" - else: - text = "no" - elif isinstance(res, str): - text = res - else: - raise TypeError("Expected string, int or bool, got " + str(type(res))) - - if self.did_show_result == 0: - # Didn't show result yet, do it now. - self.Display(text + "\n") - self.did_show_result = 1 - - def TryBuild(self, *args, **kw): - return self.sconf.TryBuild(*args, **kw) - - def TryAction(self, *args, **kw): - return self.sconf.TryAction(*args, **kw) - - def TryCompile(self, *args, **kw): - return self.sconf.TryCompile(*args, **kw) - - def TryLink(self, *args, **kw): - return self.sconf.TryLink(*args, **kw) - - def TryRun(self, *args, **kw): - return self.sconf.TryRun(*args, **kw) - - def __getattr__( self, attr ): - if( attr == 'env' ): - return self.sconf.env - elif( attr == 'lastTarget' ): - return self.sconf.lastTarget - else: - raise AttributeError("CheckContext instance has no attribute '%s'" % attr) - - #### Stuff used by Conftest.py (look there for explanations). - - def BuildProg(self, text, ext): - self.sconf.cached = 1 - # TODO: should use self.vardict for $CC, $CPPFLAGS, etc. - return not self.TryBuild(self.env.Program, text, ext) - - def CompileProg(self, text, ext): - self.sconf.cached = 1 - # TODO: should use self.vardict for $CC, $CPPFLAGS, etc. - return not self.TryBuild(self.env.Object, text, ext) - - def CompileSharedObject(self, text, ext): - self.sconf.cached = 1 - # TODO: should use self.vardict for $SHCC, $CPPFLAGS, etc. - return not self.TryBuild(self.env.SharedObject, text, ext) - - def RunProg(self, text, ext): - self.sconf.cached = 1 - # TODO: should use self.vardict for $CC, $CPPFLAGS, etc. - st, out = self.TryRun(text, ext) - return not st, out - - def AppendLIBS(self, lib_name_list): - oldLIBS = self.env.get( 'LIBS', [] ) - self.env.Append(LIBS = lib_name_list) - return oldLIBS - - def PrependLIBS(self, lib_name_list): - oldLIBS = self.env.get( 'LIBS', [] ) - self.env.Prepend(LIBS = lib_name_list) - return oldLIBS - - def SetLIBS(self, val): - oldLIBS = self.env.get( 'LIBS', [] ) - self.env.Replace(LIBS = val) - return oldLIBS - - def Display(self, msg): - if self.sconf.cached: - # We assume that Display is called twice for each test here - # once for the Checking for ... message and once for the result. - # The self.sconf.cached flag can only be set between those calls - msg = "(cached) " + msg - self.sconf.cached = 0 - progress_display(msg, append_newline=0) - self.Log("scons: Configure: " + msg + "\n") - - def Log(self, msg): - if self.sconf.logstream is not None: - self.sconf.logstream.write(msg) - - #### End of stuff used by Conftest.py. - - -def SConf(*args, **kw): - if kw.get(build_type, True): - kw['_depth'] = kw.get('_depth', 0) + 1 - for bt in build_types: - try: - del kw[bt] - except KeyError: - pass - return SConfBase(*args, **kw) - else: - return SCons.Util.Null() - - -def CheckFunc(context, function_name, header = None, language = None): - res = SCons.Conftest.CheckFunc(context, function_name, header = header, language = language) - context.did_show_result = 1 - return not res - -def CheckType(context, type_name, includes = "", language = None): - res = SCons.Conftest.CheckType(context, type_name, - header = includes, language = language) - context.did_show_result = 1 - return not res - -def CheckTypeSize(context, type_name, includes = "", language = None, expect = None): - res = SCons.Conftest.CheckTypeSize(context, type_name, - header = includes, language = language, - expect = expect) - context.did_show_result = 1 - return res - -def CheckDeclaration(context, declaration, includes = "", language = None): - res = SCons.Conftest.CheckDeclaration(context, declaration, - includes = includes, - language = language) - context.did_show_result = 1 - return not res - -def createIncludesFromHeaders(headers, leaveLast, include_quotes = '""'): - # used by CheckHeader and CheckLibWithHeader to produce C - #include - # statements from the specified header (list) - if not SCons.Util.is_List(headers): - headers = [headers] - l = [] - if leaveLast: - lastHeader = headers[-1] - headers = headers[:-1] - else: - lastHeader = None - for s in headers: - l.append("#include %s%s%s\n" - % (include_quotes[0], s, include_quotes[1])) - return ''.join(l), lastHeader - -def CheckHeader(context, header, include_quotes = '<>', language = None): - """ - A test for a C or C++ header file. - """ - prog_prefix, hdr_to_check = \ - createIncludesFromHeaders(header, 1, include_quotes) - res = SCons.Conftest.CheckHeader(context, hdr_to_check, prog_prefix, - language = language, - include_quotes = include_quotes) - context.did_show_result = 1 - return not res - -def CheckCC(context): - res = SCons.Conftest.CheckCC(context) - context.did_show_result = 1 - return not res - -def CheckCXX(context): - res = SCons.Conftest.CheckCXX(context) - context.did_show_result = 1 - return not res - -def CheckSHCC(context): - res = SCons.Conftest.CheckSHCC(context) - context.did_show_result = 1 - return not res - -def CheckSHCXX(context): - res = SCons.Conftest.CheckSHCXX(context) - context.did_show_result = 1 - return not res - -# Bram: Make this function obsolete? CheckHeader() is more generic. - -def CheckCHeader(context, header, include_quotes = '""'): - """ - A test for a C header file. - """ - return CheckHeader(context, header, include_quotes, language = "C") - - -# Bram: Make this function obsolete? CheckHeader() is more generic. - -def CheckCXXHeader(context, header, include_quotes = '""'): - """ - A test for a C++ header file. - """ - return CheckHeader(context, header, include_quotes, language = "C++") - - -def CheckLib(context, library = None, symbol = "main", - header = None, language = None, autoadd = 1): - """ - A test for a library. See also CheckLibWithHeader. - Note that library may also be None to test whether the given symbol - compiles without flags. - """ - - if library == []: - library = [None] - - if not SCons.Util.is_List(library): - library = [library] - - # ToDo: accept path for the library - res = SCons.Conftest.CheckLib(context, library, symbol, header = header, - language = language, autoadd = autoadd) - context.did_show_result = 1 - return not res - -# XXX -# Bram: Can only include one header and can't use #ifdef HAVE_HEADER_H. - -def CheckLibWithHeader(context, libs, header, language, - call = None, autoadd = 1): - # ToDo: accept path for library. Support system header files. - """ - Another (more sophisticated) test for a library. - Checks, if library and header is available for language (may be 'C' - or 'CXX'). Call maybe be a valid expression _with_ a trailing ';'. - As in CheckLib, we support library=None, to test if the call compiles - without extra link flags. - """ - prog_prefix, dummy = \ - createIncludesFromHeaders(header, 0) - if libs == []: - libs = [None] - - if not SCons.Util.is_List(libs): - libs = [libs] - - res = SCons.Conftest.CheckLib(context, libs, None, prog_prefix, - call = call, language = language, autoadd = autoadd) - context.did_show_result = 1 - return not res - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/SConsign.py b/scons/scons-local-2.2.0/SCons/SConsign.py deleted file mode 100644 index aa59836b9..000000000 --- a/scons/scons-local-2.2.0/SCons/SConsign.py +++ /dev/null @@ -1,389 +0,0 @@ -"""SCons.SConsign - -Writing and reading information to the .sconsign file or files. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/SConsign.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.compat - -import os -# compat layer imports "cPickle" for us if it's available. -import pickle - -import SCons.dblite -import SCons.Warnings - -def corrupt_dblite_warning(filename): - SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning, - "Ignoring corrupt .sconsign file: %s"%filename) - -SCons.dblite.ignore_corrupt_dbfiles = 1 -SCons.dblite.corruption_warning = corrupt_dblite_warning - -#XXX Get rid of the global array so this becomes re-entrant. -sig_files = [] - -# Info for the database SConsign implementation (now the default): -# "DataBase" is a dictionary that maps top-level SConstruct directories -# to open database handles. -# "DB_Module" is the Python database module to create the handles. -# "DB_Name" is the base name of the database file (minus any -# extension the underlying DB module will add). -DataBase = {} -DB_Module = SCons.dblite -DB_Name = ".sconsign" -DB_sync_list = [] - -def Get_DataBase(dir): - global DataBase, DB_Module, DB_Name - top = dir.fs.Top - if not os.path.isabs(DB_Name) and top.repositories: - mode = "c" - for d in [top] + top.repositories: - if dir.is_under(d): - try: - return DataBase[d], mode - except KeyError: - path = d.entry_abspath(DB_Name) - try: db = DataBase[d] = DB_Module.open(path, mode) - except (IOError, OSError): pass - else: - if mode != "r": - DB_sync_list.append(db) - return db, mode - mode = "r" - try: - return DataBase[top], "c" - except KeyError: - db = DataBase[top] = DB_Module.open(DB_Name, "c") - DB_sync_list.append(db) - return db, "c" - except TypeError: - print "DataBase =", DataBase - raise - -def Reset(): - """Reset global state. Used by unit tests that end up using - SConsign multiple times to get a clean slate for each test.""" - global sig_files, DB_sync_list - sig_files = [] - DB_sync_list = [] - -normcase = os.path.normcase - -def write(): - global sig_files - for sig_file in sig_files: - sig_file.write(sync=0) - for db in DB_sync_list: - try: - syncmethod = db.sync - except AttributeError: - pass # Not all dbm modules have sync() methods. - else: - syncmethod() - try: - closemethod = db.close - except AttributeError: - pass # Not all dbm modules have close() methods. - else: - closemethod() - -class SConsignEntry(object): - """ - Wrapper class for the generic entry in a .sconsign file. - The Node subclass populates it with attributes as it pleases. - - XXX As coded below, we do expect a '.binfo' attribute to be added, - but we'll probably generalize this in the next refactorings. - """ - current_version_id = 1 - def __init__(self): - # Create an object attribute from the class attribute so it ends up - # in the pickled data in the .sconsign file. - _version_id = self.current_version_id - def convert_to_sconsign(self): - self.binfo.convert_to_sconsign() - def convert_from_sconsign(self, dir, name): - self.binfo.convert_from_sconsign(dir, name) - -class Base(object): - """ - This is the controlling class for the signatures for the collection of - entries associated with a specific directory. The actual directory - association will be maintained by a subclass that is specific to - the underlying storage method. This class provides a common set of - methods for fetching and storing the individual bits of information - that make up signature entry. - """ - def __init__(self): - self.entries = {} - self.dirty = False - self.to_be_merged = {} - - def get_entry(self, filename): - """ - Fetch the specified entry attribute. - """ - return self.entries[filename] - - def set_entry(self, filename, obj): - """ - Set the entry. - """ - self.entries[filename] = obj - self.dirty = True - - def do_not_set_entry(self, filename, obj): - pass - - def store_info(self, filename, node): - entry = node.get_stored_info() - entry.binfo.merge(node.get_binfo()) - self.to_be_merged[filename] = node - self.dirty = True - - def do_not_store_info(self, filename, node): - pass - - def merge(self): - for key, node in self.to_be_merged.items(): - entry = node.get_stored_info() - try: - ninfo = entry.ninfo - except AttributeError: - # This happens with SConf Nodes, because the configuration - # subsystem takes direct control over how the build decision - # is made and its information stored. - pass - else: - ninfo.merge(node.get_ninfo()) - self.entries[key] = entry - self.to_be_merged = {} - -class DB(Base): - """ - A Base subclass that reads and writes signature information - from a global .sconsign.db* file--the actual file suffix is - determined by the database module. - """ - def __init__(self, dir): - Base.__init__(self) - - self.dir = dir - - db, mode = Get_DataBase(dir) - - # Read using the path relative to the top of the Repository - # (self.dir.tpath) from which we're fetching the signature - # information. - path = normcase(dir.tpath) - try: - rawentries = db[path] - except KeyError: - pass - else: - try: - self.entries = pickle.loads(rawentries) - if not isinstance(self.entries, dict): - self.entries = {} - raise TypeError - except KeyboardInterrupt: - raise - except Exception, e: - SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning, - "Ignoring corrupt sconsign entry : %s (%s)\n"%(self.dir.tpath, e)) - for key, entry in self.entries.items(): - entry.convert_from_sconsign(dir, key) - - if mode == "r": - # This directory is actually under a repository, which means - # likely they're reaching in directly for a dependency on - # a file there. Don't actually set any entry info, so we - # won't try to write to that .sconsign.dblite file. - self.set_entry = self.do_not_set_entry - self.store_info = self.do_not_store_info - - global sig_files - sig_files.append(self) - - def write(self, sync=1): - if not self.dirty: - return - - self.merge() - - db, mode = Get_DataBase(self.dir) - - # Write using the path relative to the top of the SConstruct - # directory (self.dir.path), not relative to the top of - # the Repository; we only write to our own .sconsign file, - # not to .sconsign files in Repositories. - path = normcase(self.dir.path) - for key, entry in self.entries.items(): - entry.convert_to_sconsign() - db[path] = pickle.dumps(self.entries, 1) - - if sync: - try: - syncmethod = db.sync - except AttributeError: - # Not all anydbm modules have sync() methods. - pass - else: - syncmethod() - -class Dir(Base): - def __init__(self, fp=None, dir=None): - """ - fp - file pointer to read entries from - """ - Base.__init__(self) - - if not fp: - return - - self.entries = pickle.load(fp) - if not isinstance(self.entries, dict): - self.entries = {} - raise TypeError - - if dir: - for key, entry in self.entries.items(): - entry.convert_from_sconsign(dir, key) - -class DirFile(Dir): - """ - Encapsulates reading and writing a per-directory .sconsign file. - """ - def __init__(self, dir): - """ - dir - the directory for the file - """ - - self.dir = dir - self.sconsign = os.path.join(dir.path, '.sconsign') - - try: - fp = open(self.sconsign, 'rb') - except IOError: - fp = None - - try: - Dir.__init__(self, fp, dir) - except KeyboardInterrupt: - raise - except: - SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning, - "Ignoring corrupt .sconsign file: %s"%self.sconsign) - - global sig_files - sig_files.append(self) - - def write(self, sync=1): - """ - Write the .sconsign file to disk. - - Try to write to a temporary file first, and rename it if we - succeed. If we can't write to the temporary file, it's - probably because the directory isn't writable (and if so, - how did we build anything in this directory, anyway?), so - try to write directly to the .sconsign file as a backup. - If we can't rename, try to copy the temporary contents back - to the .sconsign file. Either way, always try to remove - the temporary file at the end. - """ - if not self.dirty: - return - - self.merge() - - temp = os.path.join(self.dir.path, '.scons%d' % os.getpid()) - try: - file = open(temp, 'wb') - fname = temp - except IOError: - try: - file = open(self.sconsign, 'wb') - fname = self.sconsign - except IOError: - return - for key, entry in self.entries.items(): - entry.convert_to_sconsign() - pickle.dump(self.entries, file, 1) - file.close() - if fname != self.sconsign: - try: - mode = os.stat(self.sconsign)[0] - os.chmod(self.sconsign, 0666) - os.unlink(self.sconsign) - except (IOError, OSError): - # Try to carry on in the face of either OSError - # (things like permission issues) or IOError (disk - # or network issues). If there's a really dangerous - # issue, it should get re-raised by the calls below. - pass - try: - os.rename(fname, self.sconsign) - except OSError: - # An OSError failure to rename may indicate something - # like the directory has no write permission, but - # the .sconsign file itself might still be writable, - # so try writing on top of it directly. An IOError - # here, or in any of the following calls, would get - # raised, indicating something like a potentially - # serious disk or network issue. - open(self.sconsign, 'wb').write(open(fname, 'rb').read()) - os.chmod(self.sconsign, mode) - try: - os.unlink(temp) - except (IOError, OSError): - pass - -ForDirectory = DB - -def File(name, dbm_module=None): - """ - Arrange for all signatures to be stored in a global .sconsign.db* - file. - """ - global ForDirectory, DB_Name, DB_Module - if name is None: - ForDirectory = DirFile - DB_Module = None - else: - ForDirectory = DB - DB_Name = name - if not dbm_module is None: - DB_Module = dbm_module - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Scanner/C.py b/scons/scons-local-2.2.0/SCons/Scanner/C.py deleted file mode 100644 index 6ba7eeac1..000000000 --- a/scons/scons-local-2.2.0/SCons/Scanner/C.py +++ /dev/null @@ -1,132 +0,0 @@ -"""SCons.Scanner.C - -This module implements the depenency scanner for C/C++ code. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Scanner/C.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Node.FS -import SCons.Scanner -import SCons.Util - -import SCons.cpp - -class SConsCPPScanner(SCons.cpp.PreProcessor): - """ - SCons-specific subclass of the cpp.py module's processing. - - We subclass this so that: 1) we can deal with files represented - by Nodes, not strings; 2) we can keep track of the files that are - missing. - """ - def __init__(self, *args, **kw): - SCons.cpp.PreProcessor.__init__(self, *args, **kw) - self.missing = [] - def initialize_result(self, fname): - self.result = SCons.Util.UniqueList([fname]) - def finalize_result(self, fname): - return self.result[1:] - def find_include_file(self, t): - keyword, quote, fname = t - result = SCons.Node.FS.find_file(fname, self.searchpath[quote]) - if not result: - self.missing.append((fname, self.current_file)) - return result - def read_file(self, file): - try: - fp = open(str(file.rfile())) - except EnvironmentError, e: - self.missing.append((file, self.current_file)) - return '' - else: - return fp.read() - -def dictify_CPPDEFINES(env): - cppdefines = env.get('CPPDEFINES', {}) - if cppdefines is None: - return {} - if SCons.Util.is_Sequence(cppdefines): - result = {} - for c in cppdefines: - if SCons.Util.is_Sequence(c): - result[c[0]] = c[1] - else: - result[c] = None - return result - if not SCons.Util.is_Dict(cppdefines): - return {cppdefines : None} - return cppdefines - -class SConsCPPScannerWrapper(object): - """ - The SCons wrapper around a cpp.py scanner. - - This is the actual glue between the calling conventions of generic - SCons scanners, and the (subclass of) cpp.py class that knows how - to look for #include lines with reasonably real C-preprocessor-like - evaluation of #if/#ifdef/#else/#elif lines. - """ - def __init__(self, name, variable): - self.name = name - self.path = SCons.Scanner.FindPathDirs(variable) - def __call__(self, node, env, path = ()): - cpp = SConsCPPScanner(current = node.get_dir(), - cpppath = path, - dict = dictify_CPPDEFINES(env)) - result = cpp(node) - for included, includer in cpp.missing: - fmt = "No dependency generated for file: %s (included from: %s) -- file not found" - SCons.Warnings.warn(SCons.Warnings.DependencyWarning, - fmt % (included, includer)) - return result - - def recurse_nodes(self, nodes): - return nodes - def select(self, node): - return self - -def CScanner(): - """Return a prototype Scanner instance for scanning source files - that use the C pre-processor""" - - # Here's how we would (or might) use the CPP scanner code above that - # knows how to evaluate #if/#ifdef/#else/#elif lines when searching - # for #includes. This is commented out for now until we add the - # right configurability to let users pick between the scanners. - #return SConsCPPScannerWrapper("CScanner", "CPPPATH") - - cs = SCons.Scanner.ClassicCPP("CScanner", - "$CPPSUFFIXES", - "CPPPATH", - '^[ \t]*#[ \t]*(?:include|import)[ \t]*(<|")([^>"]+)(>|")') - return cs - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Scanner/D.py b/scons/scons-local-2.2.0/SCons/Scanner/D.py deleted file mode 100644 index 20ab5f0ac..000000000 --- a/scons/scons-local-2.2.0/SCons/Scanner/D.py +++ /dev/null @@ -1,73 +0,0 @@ -"""SCons.Scanner.D - -Scanner for the Digital Mars "D" programming language. - -Coded by Andy Friesen -17 Nov 2003 - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Scanner/D.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import re - -import SCons.Scanner - -def DScanner(): - """Return a prototype Scanner instance for scanning D source files""" - ds = D() - return ds - -class D(SCons.Scanner.Classic): - def __init__ (self): - SCons.Scanner.Classic.__init__ (self, - name = "DScanner", - suffixes = '$DSUFFIXES', - path_variable = 'DPATH', - regex = 'import\s+(?:[a-zA-Z0-9_.]+)\s*(?:,\s*(?:[a-zA-Z0-9_.]+)\s*)*;') - - self.cre2 = re.compile ('(?:import\s)?\s*([a-zA-Z0-9_.]+)\s*(?:,|;)', re.M) - - def find_include(self, include, source_dir, path): - # translate dots (package separators) to slashes - inc = include.replace('.', '/') - - i = SCons.Node.FS.find_file(inc + '.d', (source_dir,) + path) - if i is None: - i = SCons.Node.FS.find_file (inc + '.di', (source_dir,) + path) - return i, include - - def find_include_names(self, node): - includes = [] - for i in self.cre.findall(node.get_text_contents()): - includes = includes + self.cre2.findall(i) - return includes - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Scanner/Dir.py b/scons/scons-local-2.2.0/SCons/Scanner/Dir.py deleted file mode 100644 index 7c199bcf3..000000000 --- a/scons/scons-local-2.2.0/SCons/Scanner/Dir.py +++ /dev/null @@ -1,109 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Scanner/Dir.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Node.FS -import SCons.Scanner - -def only_dirs(nodes): - is_Dir = lambda n: isinstance(n.disambiguate(), SCons.Node.FS.Dir) - return list(filter(is_Dir, nodes)) - -def DirScanner(**kw): - """Return a prototype Scanner instance for scanning - directories for on-disk files""" - kw['node_factory'] = SCons.Node.FS.Entry - kw['recursive'] = only_dirs - return SCons.Scanner.Base(scan_on_disk, "DirScanner", **kw) - -def DirEntryScanner(**kw): - """Return a prototype Scanner instance for "scanning" - directory Nodes for their in-memory entries""" - kw['node_factory'] = SCons.Node.FS.Entry - kw['recursive'] = None - return SCons.Scanner.Base(scan_in_memory, "DirEntryScanner", **kw) - -skip_entry = {} - -skip_entry_list = [ - '.', - '..', - '.sconsign', - # Used by the native dblite.py module. - '.sconsign.dblite', - # Used by dbm and dumbdbm. - '.sconsign.dir', - # Used by dbm. - '.sconsign.pag', - # Used by dumbdbm. - '.sconsign.dat', - '.sconsign.bak', - # Used by some dbm emulations using Berkeley DB. - '.sconsign.db', -] - -for skip in skip_entry_list: - skip_entry[skip] = 1 - skip_entry[SCons.Node.FS._my_normcase(skip)] = 1 - -do_not_scan = lambda k: k not in skip_entry - -def scan_on_disk(node, env, path=()): - """ - Scans a directory for on-disk files and directories therein. - - Looking up the entries will add these to the in-memory Node tree - representation of the file system, so all we have to do is just - that and then call the in-memory scanning function. - """ - try: - flist = node.fs.listdir(node.abspath) - except (IOError, OSError): - return [] - e = node.Entry - for f in filter(do_not_scan, flist): - # Add ./ to the beginning of the file name so if it begins with a - # '#' we don't look it up relative to the top-level directory. - e('./' + f) - return scan_in_memory(node, env, path) - -def scan_in_memory(node, env, path=()): - """ - "Scans" a Node.FS.Dir for its in-memory entries. - """ - try: - entries = node.entries - except AttributeError: - # It's not a Node.FS.Dir (or doesn't look enough like one for - # our purposes), which can happen if a target list containing - # mixed Node types (Dirs and Files, for example) has a Dir as - # the first entry. - return [] - entry_list = sorted(filter(do_not_scan, list(entries.keys()))) - return [entries[n] for n in entry_list] - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Scanner/Fortran.py b/scons/scons-local-2.2.0/SCons/Scanner/Fortran.py deleted file mode 100644 index 4bb49cd78..000000000 --- a/scons/scons-local-2.2.0/SCons/Scanner/Fortran.py +++ /dev/null @@ -1,316 +0,0 @@ -"""SCons.Scanner.Fortran - -This module implements the dependency scanner for Fortran code. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Scanner/Fortran.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import re - -import SCons.Node -import SCons.Node.FS -import SCons.Scanner -import SCons.Util -import SCons.Warnings - -class F90Scanner(SCons.Scanner.Classic): - """ - A Classic Scanner subclass for Fortran source files which takes - into account both USE and INCLUDE statements. This scanner will - work for both F77 and F90 (and beyond) compilers. - - Currently, this scanner assumes that the include files do not contain - USE statements. To enable the ability to deal with USE statements - in include files, add logic right after the module names are found - to loop over each include file, search for and locate each USE - statement, and append each module name to the list of dependencies. - Caching the search results in a common dictionary somewhere so that - the same include file is not searched multiple times would be a - smart thing to do. - """ - - def __init__(self, name, suffixes, path_variable, - use_regex, incl_regex, def_regex, *args, **kw): - - self.cre_use = re.compile(use_regex, re.M) - self.cre_incl = re.compile(incl_regex, re.M) - self.cre_def = re.compile(def_regex, re.M) - - def _scan(node, env, path, self=self): - node = node.rfile() - - if not node.exists(): - return [] - - return self.scan(node, env, path) - - kw['function'] = _scan - kw['path_function'] = SCons.Scanner.FindPathDirs(path_variable) - kw['recursive'] = 1 - kw['skeys'] = suffixes - kw['name'] = name - - SCons.Scanner.Current.__init__(self, *args, **kw) - - def scan(self, node, env, path=()): - - # cache the includes list in node so we only scan it once: - if node.includes != None: - mods_and_includes = node.includes - else: - # retrieve all included filenames - includes = self.cre_incl.findall(node.get_text_contents()) - # retrieve all USE'd module names - modules = self.cre_use.findall(node.get_text_contents()) - # retrieve all defined module names - defmodules = self.cre_def.findall(node.get_text_contents()) - - # Remove all USE'd module names that are defined in the same file - # (case-insensitively) - d = {} - for m in defmodules: - d[m.lower()] = 1 - modules = [m for m in modules if m.lower() not in d] - - # Convert module name to a .mod filename - suffix = env.subst('$FORTRANMODSUFFIX') - modules = [x.lower() + suffix for x in modules] - # Remove unique items from the list - mods_and_includes = SCons.Util.unique(includes+modules) - node.includes = mods_and_includes - - # This is a hand-coded DSU (decorate-sort-undecorate, or - # Schwartzian transform) pattern. The sort key is the raw name - # of the file as specifed on the USE or INCLUDE line, which lets - # us keep the sort order constant regardless of whether the file - # is actually found in a Repository or locally. - nodes = [] - source_dir = node.get_dir() - if callable(path): - path = path() - for dep in mods_and_includes: - n, i = self.find_include(dep, source_dir, path) - - if n is None: - SCons.Warnings.warn(SCons.Warnings.DependencyWarning, - "No dependency generated for file: %s (referenced by: %s) -- file not found" % (i, node)) - else: - sortkey = self.sort_key(dep) - nodes.append((sortkey, n)) - - return [pair[1] for pair in sorted(nodes)] - -def FortranScan(path_variable="FORTRANPATH"): - """Return a prototype Scanner instance for scanning source files - for Fortran USE & INCLUDE statements""" - -# The USE statement regex matches the following: -# -# USE module_name -# USE :: module_name -# USE, INTRINSIC :: module_name -# USE, NON_INTRINSIC :: module_name -# -# Limitations -# -# -- While the regex can handle multiple USE statements on one line, -# it cannot properly handle them if they are commented out. -# In either of the following cases: -# -# ! USE mod_a ; USE mod_b [entire line is commented out] -# USE mod_a ! ; USE mod_b [in-line comment of second USE statement] -# -# the second module name (mod_b) will be picked up as a dependency -# even though it should be ignored. The only way I can see -# to rectify this would be to modify the scanner to eliminate -# the call to re.findall, read in the contents of the file, -# treating the comment character as an end-of-line character -# in addition to the normal linefeed, loop over each line, -# weeding out the comments, and looking for the USE statements. -# One advantage to this is that the regex passed to the scanner -# would no longer need to match a semicolon. -# -# -- I question whether or not we need to detect dependencies to -# INTRINSIC modules because these are built-in to the compiler. -# If we consider them a dependency, will SCons look for them, not -# find them, and kill the build? Or will we there be standard -# compiler-specific directories we will need to point to so the -# compiler and SCons can locate the proper object and mod files? - -# Here is a breakdown of the regex: -# -# (?i) : regex is case insensitive -# ^ : start of line -# (?: : group a collection of regex symbols without saving the match as a "group" -# ^|; : matches either the start of the line or a semicolon - semicolon -# ) : end the unsaved grouping -# \s* : any amount of white space -# USE : match the string USE, case insensitive -# (?: : group a collection of regex symbols without saving the match as a "group" -# \s+| : match one or more whitespace OR .... (the next entire grouped set of regex symbols) -# (?: : group a collection of regex symbols without saving the match as a "group" -# (?: : establish another unsaved grouping of regex symbols -# \s* : any amount of white space -# , : match a comma -# \s* : any amount of white space -# (?:NON_)? : optionally match the prefix NON_, case insensitive -# INTRINSIC : match the string INTRINSIC, case insensitive -# )? : optionally match the ", INTRINSIC/NON_INTRINSIC" grouped expression -# \s* : any amount of white space -# :: : match a double colon that must appear after the INTRINSIC/NON_INTRINSIC attribute -# ) : end the unsaved grouping -# ) : end the unsaved grouping -# \s* : match any amount of white space -# (\w+) : match the module name that is being USE'd -# -# - use_regex = "(?i)(?:^|;)\s*USE(?:\s+|(?:(?:\s*,\s*(?:NON_)?INTRINSIC)?\s*::))\s*(\w+)" - - -# The INCLUDE statement regex matches the following: -# -# INCLUDE 'some_Text' -# INCLUDE "some_Text" -# INCLUDE "some_Text" ; INCLUDE "some_Text" -# INCLUDE kind_"some_Text" -# INCLUDE kind_'some_Text" -# -# where some_Text can include any alphanumeric and/or special character -# as defined by the Fortran 2003 standard. -# -# Limitations: -# -# -- The Fortran standard dictates that a " or ' in the INCLUDE'd -# string must be represented as a "" or '', if the quotes that wrap -# the entire string are either a ' or ", respectively. While the -# regular expression below can detect the ' or " characters just fine, -# the scanning logic, presently is unable to detect them and reduce -# them to a single instance. This probably isn't an issue since, -# in practice, ' or " are not generally used in filenames. -# -# -- This regex will not properly deal with multiple INCLUDE statements -# when the entire line has been commented out, ala -# -# ! INCLUDE 'some_file' ; INCLUDE 'some_file' -# -# In such cases, it will properly ignore the first INCLUDE file, -# but will actually still pick up the second. Interestingly enough, -# the regex will properly deal with these cases: -# -# INCLUDE 'some_file' -# INCLUDE 'some_file' !; INCLUDE 'some_file' -# -# To get around the above limitation, the FORTRAN programmer could -# simply comment each INCLUDE statement separately, like this -# -# ! INCLUDE 'some_file' !; INCLUDE 'some_file' -# -# The way I see it, the only way to get around this limitation would -# be to modify the scanning logic to replace the calls to re.findall -# with a custom loop that processes each line separately, throwing -# away fully commented out lines before attempting to match against -# the INCLUDE syntax. -# -# Here is a breakdown of the regex: -# -# (?i) : regex is case insensitive -# (?: : begin a non-saving group that matches the following: -# ^ : either the start of the line -# | : or -# ['">]\s*; : a semicolon that follows a single quote, -# double quote or greater than symbol (with any -# amount of whitespace in between). This will -# allow the regex to match multiple INCLUDE -# statements per line (although it also requires -# the positive lookahead assertion that is -# used below). It will even properly deal with -# (i.e. ignore) cases in which the additional -# INCLUDES are part of an in-line comment, ala -# " INCLUDE 'someFile' ! ; INCLUDE 'someFile2' " -# ) : end of non-saving group -# \s* : any amount of white space -# INCLUDE : match the string INCLUDE, case insensitive -# \s+ : match one or more white space characters -# (?\w+_)? : match the optional "kind-param _" prefix allowed by the standard -# [<"'] : match the include delimiter - an apostrophe, double quote, or less than symbol -# (.+?) : match one or more characters that make up -# the included path and file name and save it -# in a group. The Fortran standard allows for -# any non-control character to be used. The dot -# operator will pick up any character, including -# control codes, but I can't conceive of anyone -# putting control codes in their file names. -# The question mark indicates it is non-greedy so -# that regex will match only up to the next quote, -# double quote, or greater than symbol -# (?=["'>]) : positive lookahead assertion to match the include -# delimiter - an apostrophe, double quote, or -# greater than symbol. This level of complexity -# is required so that the include delimiter is -# not consumed by the match, thus allowing the -# sub-regex discussed above to uniquely match a -# set of semicolon-separated INCLUDE statements -# (as allowed by the F2003 standard) - - include_regex = """(?i)(?:^|['">]\s*;)\s*INCLUDE\s+(?:\w+_)?[<"'](.+?)(?=["'>])""" - -# The MODULE statement regex finds module definitions by matching -# the following: -# -# MODULE module_name -# -# but *not* the following: -# -# MODULE PROCEDURE procedure_name -# -# Here is a breakdown of the regex: -# -# (?i) : regex is case insensitive -# ^\s* : any amount of white space -# MODULE : match the string MODULE, case insensitive -# \s+ : match one or more white space characters -# (?!PROCEDURE) : but *don't* match if the next word matches -# PROCEDURE (negative lookahead assertion), -# case insensitive -# (\w+) : match one or more alphanumeric characters -# that make up the defined module name and -# save it in a group - - def_regex = """(?i)^\s*MODULE\s+(?!PROCEDURE)(\w+)""" - - scanner = F90Scanner("FortranScan", - "$FORTRANSUFFIXES", - path_variable, - use_regex, - include_regex, - def_regex) - return scanner - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Scanner/IDL.py b/scons/scons-local-2.2.0/SCons/Scanner/IDL.py deleted file mode 100644 index d43e0139d..000000000 --- a/scons/scons-local-2.2.0/SCons/Scanner/IDL.py +++ /dev/null @@ -1,48 +0,0 @@ -"""SCons.Scanner.IDL - -This module implements the depenency scanner for IDL (Interface -Definition Language) files. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Scanner/IDL.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Node.FS -import SCons.Scanner - -def IDLScan(): - """Return a prototype Scanner instance for scanning IDL source files""" - cs = SCons.Scanner.ClassicCPP("IDLScan", - "$IDLSUFFIXES", - "CPPPATH", - '^[ \t]*(?:#[ \t]*include|[ \t]*import)[ \t]+(<|")([^>"]+)(>|")') - return cs - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Scanner/LaTeX.py b/scons/scons-local-2.2.0/SCons/Scanner/LaTeX.py deleted file mode 100644 index 4152fa2ae..000000000 --- a/scons/scons-local-2.2.0/SCons/Scanner/LaTeX.py +++ /dev/null @@ -1,390 +0,0 @@ -"""SCons.Scanner.LaTeX - -This module implements the dependency scanner for LaTeX code. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Scanner/LaTeX.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os.path -import re - -import SCons.Scanner -import SCons.Util - -# list of graphics file extensions for TeX and LaTeX -TexGraphics = ['.eps', '.ps'] -LatexGraphics = ['.pdf', '.png', '.jpg', '.gif', '.tif'] - -# Used as a return value of modify_env_var if the variable is not set. -class _Null(object): - pass -_null = _Null - -# The user specifies the paths in env[variable], similar to other builders. -# They may be relative and must be converted to absolute, as expected -# by LaTeX and Co. The environment may already have some paths in -# env['ENV'][var]. These paths are honored, but the env[var] paths have -# higher precedence. All changes are un-done on exit. -def modify_env_var(env, var, abspath): - try: - save = env['ENV'][var] - except KeyError: - save = _null - env.PrependENVPath(var, abspath) - try: - if SCons.Util.is_List(env[var]): - env.PrependENVPath(var, [os.path.abspath(str(p)) for p in env[var]]) - else: - # Split at os.pathsep to convert into absolute path - env.PrependENVPath(var, [os.path.abspath(p) for p in str(env[var]).split(os.pathsep)]) - except KeyError: - pass - - # Convert into a string explicitly to append ":" (without which it won't search system - # paths as well). The problem is that env.AppendENVPath(var, ":") - # does not work, refuses to append ":" (os.pathsep). - - if SCons.Util.is_List(env['ENV'][var]): - env['ENV'][var] = os.pathsep.join(env['ENV'][var]) - # Append the trailing os.pathsep character here to catch the case with no env[var] - env['ENV'][var] = env['ENV'][var] + os.pathsep - - return save - -class FindENVPathDirs(object): - """A class to bind a specific *PATH variable name to a function that - will return all of the *path directories.""" - def __init__(self, variable): - self.variable = variable - def __call__(self, env, dir=None, target=None, source=None, argument=None): - import SCons.PathList - try: - path = env['ENV'][self.variable] - except KeyError: - return () - - dir = dir or env.fs._cwd - path = SCons.PathList.PathList(path).subst_path(env, target, source) - return tuple(dir.Rfindalldirs(path)) - - - -def LaTeXScanner(): - """Return a prototype Scanner instance for scanning LaTeX source files - when built with latex. - """ - ds = LaTeX(name = "LaTeXScanner", - suffixes = '$LATEXSUFFIXES', - # in the search order, see below in LaTeX class docstring - graphics_extensions = TexGraphics, - recursive = 0) - return ds - -def PDFLaTeXScanner(): - """Return a prototype Scanner instance for scanning LaTeX source files - when built with pdflatex. - """ - ds = LaTeX(name = "PDFLaTeXScanner", - suffixes = '$LATEXSUFFIXES', - # in the search order, see below in LaTeX class docstring - graphics_extensions = LatexGraphics, - recursive = 0) - return ds - -class LaTeX(SCons.Scanner.Base): - """Class for scanning LaTeX files for included files. - - Unlike most scanners, which use regular expressions that just - return the included file name, this returns a tuple consisting - of the keyword for the inclusion ("include", "includegraphics", - "input", or "bibliography"), and then the file name itself. - Based on a quick look at LaTeX documentation, it seems that we - should append .tex suffix for the "include" keywords, append .tex if - there is no extension for the "input" keyword, and need to add .bib - for the "bibliography" keyword that does not accept extensions by itself. - - Finally, if there is no extension for an "includegraphics" keyword - latex will append .ps or .eps to find the file, while pdftex may use .pdf, - .jpg, .tif, .mps, or .png. - - The actual subset and search order may be altered by - DeclareGraphicsExtensions command. This complication is ignored. - The default order corresponds to experimentation with teTeX - $ latex --version - pdfeTeX 3.141592-1.21a-2.2 (Web2C 7.5.4) - kpathsea version 3.5.4 - The order is: - ['.eps', '.ps'] for latex - ['.png', '.pdf', '.jpg', '.tif']. - - Another difference is that the search path is determined by the type - of the file being searched: - env['TEXINPUTS'] for "input" and "include" keywords - env['TEXINPUTS'] for "includegraphics" keyword - env['TEXINPUTS'] for "lstinputlisting" keyword - env['BIBINPUTS'] for "bibliography" keyword - env['BSTINPUTS'] for "bibliographystyle" keyword - env['INDEXSTYLE'] for "makeindex" keyword, no scanning support needed - just allows user to set it if needed. - - FIXME: also look for the class or style in document[class|style]{} - FIXME: also look for the argument of bibliographystyle{} - """ - keyword_paths = {'include': 'TEXINPUTS', - 'input': 'TEXINPUTS', - 'includegraphics': 'TEXINPUTS', - 'bibliography': 'BIBINPUTS', - 'bibliographystyle': 'BSTINPUTS', - 'addbibresource': 'BIBINPUTS', - 'addglobalbib': 'BIBINPUTS', - 'addsectionbib': 'BIBINPUTS', - 'makeindex': 'INDEXSTYLE', - 'usepackage': 'TEXINPUTS', - 'lstinputlisting': 'TEXINPUTS'} - env_variables = SCons.Util.unique(list(keyword_paths.values())) - - def __init__(self, name, suffixes, graphics_extensions, *args, **kw): - - # We have to include \n with the % we exclude from the first part - # part of the regex because the expression is compiled with re.M. - # Without the \n, the ^ could match the beginning of a *previous* - # line followed by one or more newline characters (i.e. blank - # lines), interfering with a match on the next line. - # add option for whitespace before the '[options]' or the '{filename}' - regex = r'^[^%\n]*\\(include|includegraphics(?:\s*\[[^\]]+\])?|lstinputlisting(?:\[[^\]]+\])?|input|bibliography|addbibresource|addglobalbib|addsectionbib|usepackage)\s*{([^}]*)}' - self.cre = re.compile(regex, re.M) - self.comment_re = re.compile(r'^((?:(?:\\%)|[^%\n])*)(.*)$', re.M) - - self.graphics_extensions = graphics_extensions - - def _scan(node, env, path=(), self=self): - node = node.rfile() - if not node.exists(): - return [] - return self.scan_recurse(node, path) - - class FindMultiPathDirs(object): - """The stock FindPathDirs function has the wrong granularity: - it is called once per target, while we need the path that depends - on what kind of included files is being searched. This wrapper - hides multiple instances of FindPathDirs, one per the LaTeX path - variable in the environment. When invoked, the function calculates - and returns all the required paths as a dictionary (converted into - a tuple to become hashable). Then the scan function converts it - back and uses a dictionary of tuples rather than a single tuple - of paths. - """ - def __init__(self, dictionary): - self.dictionary = {} - for k,n in dictionary.items(): - self.dictionary[k] = ( SCons.Scanner.FindPathDirs(n), - FindENVPathDirs(n) ) - - def __call__(self, env, dir=None, target=None, source=None, - argument=None): - di = {} - for k,(c,cENV) in self.dictionary.items(): - di[k] = ( c(env, dir=None, target=None, source=None, - argument=None) , - cENV(env, dir=None, target=None, source=None, - argument=None) ) - # To prevent "dict is not hashable error" - return tuple(di.items()) - - class LaTeXScanCheck(object): - """Skip all but LaTeX source files, i.e., do not scan *.eps, - *.pdf, *.jpg, etc. - """ - def __init__(self, suffixes): - self.suffixes = suffixes - def __call__(self, node, env): - current = not node.has_builder() or node.is_up_to_date() - scannable = node.get_suffix() in env.subst_list(self.suffixes)[0] - # Returning false means that the file is not scanned. - return scannable and current - - kw['function'] = _scan - kw['path_function'] = FindMultiPathDirs(LaTeX.keyword_paths) - kw['recursive'] = 0 - kw['skeys'] = suffixes - kw['scan_check'] = LaTeXScanCheck(suffixes) - kw['name'] = name - - SCons.Scanner.Base.__init__(self, *args, **kw) - - def _latex_names(self, include): - filename = include[1] - if include[0] == 'input': - base, ext = os.path.splitext( filename ) - if ext == "": - return [filename + '.tex'] - if (include[0] == 'include'): - return [filename + '.tex'] - if include[0] == 'bibliography': - base, ext = os.path.splitext( filename ) - if ext == "": - return [filename + '.bib'] - if include[0] == 'usepackage': - base, ext = os.path.splitext( filename ) - if ext == "": - return [filename + '.sty'] - if include[0] == 'includegraphics': - base, ext = os.path.splitext( filename ) - if ext == "": - #return [filename+e for e in self.graphics_extensions + TexGraphics] - # use the line above to find dependencies for the PDF builder - # when only an .eps figure is present. Since it will be found - # if the user tells scons how to make the pdf figure, leave - # it out for now. - return [filename+e for e in self.graphics_extensions] - return [filename] - - def sort_key(self, include): - return SCons.Node.FS._my_normcase(str(include)) - - def find_include(self, include, source_dir, path): - try: - sub_path = path[include[0]] - except (IndexError, KeyError): - sub_path = () - try_names = self._latex_names(include) - for n in try_names: - # see if we find it using the path in env[var] - i = SCons.Node.FS.find_file(n, (source_dir,) + sub_path[0]) - if i: - return i, include - # see if we find it using the path in env['ENV'][var] - i = SCons.Node.FS.find_file(n, (source_dir,) + sub_path[1]) - if i: - return i, include - return i, include - - def canonical_text(self, text): - """Standardize an input TeX-file contents. - - Currently: - * removes comments, unwrapping comment-wrapped lines. - """ - out = [] - line_continues_a_comment = False - for line in text.splitlines(): - line,comment = self.comment_re.findall(line)[0] - if line_continues_a_comment == True: - out[-1] = out[-1] + line.lstrip() - else: - out.append(line) - line_continues_a_comment = len(comment) > 0 - return '\n'.join(out).rstrip()+'\n' - - def scan(self, node): - # Modify the default scan function to allow for the regular - # expression to return a comma separated list of file names - # as can be the case with the bibliography keyword. - - # Cache the includes list in node so we only scan it once: - # path_dict = dict(list(path)) - # add option for whitespace (\s) before the '[' - noopt_cre = re.compile('\s*\[.*$') - if node.includes != None: - includes = node.includes - else: - text = self.canonical_text(node.get_text_contents()) - includes = self.cre.findall(text) - # 1. Split comma-separated lines, e.g. - # ('bibliography', 'phys,comp') - # should become two entries - # ('bibliography', 'phys') - # ('bibliography', 'comp') - # 2. Remove the options, e.g., such as - # ('includegraphics[clip,width=0.7\\linewidth]', 'picture.eps') - # should become - # ('includegraphics', 'picture.eps') - split_includes = [] - for include in includes: - inc_type = noopt_cre.sub('', include[0]) - inc_list = include[1].split(',') - for j in range(len(inc_list)): - split_includes.append( (inc_type, inc_list[j]) ) - # - includes = split_includes - node.includes = includes - - return includes - - def scan_recurse(self, node, path=()): - """ do a recursive scan of the top level target file - This lets us search for included files based on the - directory of the main file just as latex does""" - - path_dict = dict(list(path)) - - queue = [] - queue.extend( self.scan(node) ) - seen = {} - - # This is a hand-coded DSU (decorate-sort-undecorate, or - # Schwartzian transform) pattern. The sort key is the raw name - # of the file as specifed on the \include, \input, etc. line. - # TODO: what about the comment in the original Classic scanner: - # """which lets - # us keep the sort order constant regardless of whether the file - # is actually found in a Repository or locally.""" - nodes = [] - source_dir = node.get_dir() - #for include in includes: - while queue: - - include = queue.pop() - try: - if seen[include[1]] == 1: - continue - except KeyError: - seen[include[1]] = 1 - - # - # Handle multiple filenames in include[1] - # - n, i = self.find_include(include, source_dir, path_dict) - if n is None: - # Do not bother with 'usepackage' warnings, as they most - # likely refer to system-level files - if include[0] != 'usepackage': - SCons.Warnings.warn(SCons.Warnings.DependencyWarning, - "No dependency generated for file: %s (included from: %s) -- file not found" % (i, node)) - else: - sortkey = self.sort_key(n) - nodes.append((sortkey, n)) - # recurse down - queue.extend( self.scan(n) ) - - return [pair[1] for pair in sorted(nodes)] - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Scanner/Prog.py b/scons/scons-local-2.2.0/SCons/Scanner/Prog.py deleted file mode 100644 index fbdf8581e..000000000 --- a/scons/scons-local-2.2.0/SCons/Scanner/Prog.py +++ /dev/null @@ -1,101 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Scanner/Prog.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Node -import SCons.Node.FS -import SCons.Scanner -import SCons.Util - -# global, set by --debug=findlibs -print_find_libs = None - -def ProgramScanner(**kw): - """Return a prototype Scanner instance for scanning executable - files for static-lib dependencies""" - kw['path_function'] = SCons.Scanner.FindPathDirs('LIBPATH') - ps = SCons.Scanner.Base(scan, "ProgramScanner", **kw) - return ps - -def scan(node, env, libpath = ()): - """ - This scanner scans program files for static-library - dependencies. It will search the LIBPATH environment variable - for libraries specified in the LIBS variable, returning any - files it finds as dependencies. - """ - try: - libs = env['LIBS'] - except KeyError: - # There are no LIBS in this environment, so just return a null list: - return [] - if SCons.Util.is_String(libs): - libs = libs.split() - else: - libs = SCons.Util.flatten(libs) - - try: - prefix = env['LIBPREFIXES'] - if not SCons.Util.is_List(prefix): - prefix = [ prefix ] - except KeyError: - prefix = [ '' ] - - try: - suffix = env['LIBSUFFIXES'] - if not SCons.Util.is_List(suffix): - suffix = [ suffix ] - except KeyError: - suffix = [ '' ] - - pairs = [] - for suf in map(env.subst, suffix): - for pref in map(env.subst, prefix): - pairs.append((pref, suf)) - - result = [] - - if callable(libpath): - libpath = libpath() - - find_file = SCons.Node.FS.find_file - adjustixes = SCons.Util.adjustixes - for lib in libs: - if SCons.Util.is_String(lib): - lib = env.subst(lib) - for pref, suf in pairs: - l = adjustixes(lib, pref, suf) - l = find_file(l, libpath, verbose=print_find_libs) - if l: - result.append(l) - else: - result.append(lib) - - return result - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Scanner/RC.py b/scons/scons-local-2.2.0/SCons/Scanner/RC.py deleted file mode 100644 index 871fdf9a0..000000000 --- a/scons/scons-local-2.2.0/SCons/Scanner/RC.py +++ /dev/null @@ -1,55 +0,0 @@ -"""SCons.Scanner.RC - -This module implements the depenency scanner for RC (Interface -Definition Language) files. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Scanner/RC.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Node.FS -import SCons.Scanner -import re - -def RCScan(): - """Return a prototype Scanner instance for scanning RC source files""" - - res_re= r'^(?:\s*#\s*(?:include)|' \ - '.*?\s+(?:ICON|BITMAP|CURSOR|HTML|FONT|MESSAGETABLE|TYPELIB|REGISTRY|D3DFX)' \ - '\s*.*?)' \ - '\s*(<|"| )([^>"\s]+)(?:[>"\s])*$' - resScanner = SCons.Scanner.ClassicCPP( "ResourceScanner", - "$RCSUFFIXES", - "CPPPATH", - res_re ) - - return resScanner - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Scanner/__init__.py b/scons/scons-local-2.2.0/SCons/Scanner/__init__.py deleted file mode 100644 index 9675e8dd5..000000000 --- a/scons/scons-local-2.2.0/SCons/Scanner/__init__.py +++ /dev/null @@ -1,413 +0,0 @@ -"""SCons.Scanner - -The Scanner package for the SCons software construction utility. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Scanner/__init__.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import re - -import SCons.Node.FS -import SCons.Util - - -class _Null(object): - pass - -# This is used instead of None as a default argument value so None can be -# used as an actual argument value. -_null = _Null - -def Scanner(function, *args, **kw): - """ - Public interface factory function for creating different types - of Scanners based on the different types of "functions" that may - be supplied. - - TODO: Deprecate this some day. We've moved the functionality - inside the Base class and really don't need this factory function - any more. It was, however, used by some of our Tool modules, so - the call probably ended up in various people's custom modules - patterned on SCons code. - """ - if SCons.Util.is_Dict(function): - return Selector(function, *args, **kw) - else: - return Base(function, *args, **kw) - - - -class FindPathDirs(object): - """A class to bind a specific *PATH variable name to a function that - will return all of the *path directories.""" - def __init__(self, variable): - self.variable = variable - def __call__(self, env, dir=None, target=None, source=None, argument=None): - import SCons.PathList - try: - path = env[self.variable] - except KeyError: - return () - - dir = dir or env.fs._cwd - path = SCons.PathList.PathList(path).subst_path(env, target, source) - return tuple(dir.Rfindalldirs(path)) - - - -class Base(object): - """ - The base class for dependency scanners. This implements - straightforward, single-pass scanning of a single file. - """ - - def __init__(self, - function, - name = "NONE", - argument = _null, - skeys = _null, - path_function = None, - # Node.FS.Base so that, by default, it's okay for a - # scanner to return a Dir, File or Entry. - node_class = SCons.Node.FS.Base, - node_factory = None, - scan_check = None, - recursive = None): - """ - Construct a new scanner object given a scanner function. - - 'function' - a scanner function taking two or three - arguments and returning a list of strings. - - 'name' - a name for identifying this scanner object. - - 'argument' - an optional argument that, if specified, will be - passed to both the scanner function and the path_function. - - 'skeys' - an optional list argument that can be used to determine - which scanner should be used for a given Node. In the case of File - nodes, for example, the 'skeys' would be file suffixes. - - 'path_function' - a function that takes four or five arguments - (a construction environment, Node for the directory containing - the SConscript file that defined the primary target, list of - target nodes, list of source nodes, and optional argument for - this instance) and returns a tuple of the directories that can - be searched for implicit dependency files. May also return a - callable() which is called with no args and returns the tuple - (supporting Bindable class). - - 'node_class' - the class of Nodes which this scan will return. - If node_class is None, then this scanner will not enforce any - Node conversion and will return the raw results from the - underlying scanner function. - - 'node_factory' - the factory function to be called to translate - the raw results returned by the scanner function into the - expected node_class objects. - - 'scan_check' - a function to be called to first check whether - this node really needs to be scanned. - - 'recursive' - specifies that this scanner should be invoked - recursively on all of the implicit dependencies it returns - (the canonical example being #include lines in C source files). - May be a callable, which will be called to filter the list - of nodes found to select a subset for recursive scanning - (the canonical example being only recursively scanning - subdirectories within a directory). - - The scanner function's first argument will be a Node that should - be scanned for dependencies, the second argument will be an - Environment object, the third argument will be the tuple of paths - returned by the path_function, and the fourth argument will be - the value passed into 'argument', and the returned list should - contain the Nodes for all the direct dependencies of the file. - - Examples: - - s = Scanner(my_scanner_function) - - s = Scanner(function = my_scanner_function) - - s = Scanner(function = my_scanner_function, argument = 'foo') - - """ - - # Note: this class could easily work with scanner functions that take - # something other than a filename as an argument (e.g. a database - # node) and a dependencies list that aren't file names. All that - # would need to be changed is the documentation. - - self.function = function - self.path_function = path_function - self.name = name - self.argument = argument - - if skeys is _null: - if SCons.Util.is_Dict(function): - skeys = list(function.keys()) - else: - skeys = [] - self.skeys = skeys - - self.node_class = node_class - self.node_factory = node_factory - self.scan_check = scan_check - if callable(recursive): - self.recurse_nodes = recursive - elif recursive: - self.recurse_nodes = self._recurse_all_nodes - else: - self.recurse_nodes = self._recurse_no_nodes - - def path(self, env, dir=None, target=None, source=None): - if not self.path_function: - return () - if not self.argument is _null: - return self.path_function(env, dir, target, source, self.argument) - else: - return self.path_function(env, dir, target, source) - - def __call__(self, node, env, path = ()): - """ - This method scans a single object. 'node' is the node - that will be passed to the scanner function, and 'env' is the - environment that will be passed to the scanner function. A list of - direct dependency nodes for the specified node will be returned. - """ - if self.scan_check and not self.scan_check(node, env): - return [] - - self = self.select(node) - - if not self.argument is _null: - list = self.function(node, env, path, self.argument) - else: - list = self.function(node, env, path) - - kw = {} - if hasattr(node, 'dir'): - kw['directory'] = node.dir - node_factory = env.get_factory(self.node_factory) - nodes = [] - for l in list: - if self.node_class and not isinstance(l, self.node_class): - l = node_factory(l, **kw) - nodes.append(l) - return nodes - - def __cmp__(self, other): - try: - return cmp(self.__dict__, other.__dict__) - except AttributeError: - # other probably doesn't have a __dict__ - return cmp(self.__dict__, other) - - def __hash__(self): - return id(self) - - def __str__(self): - return self.name - - def add_skey(self, skey): - """Add a skey to the list of skeys""" - self.skeys.append(skey) - - def get_skeys(self, env=None): - if env and SCons.Util.is_String(self.skeys): - return env.subst_list(self.skeys)[0] - return self.skeys - - def select(self, node): - if SCons.Util.is_Dict(self.function): - key = node.scanner_key() - try: - return self.function[key] - except KeyError: - return None - else: - return self - - def _recurse_all_nodes(self, nodes): - return nodes - - def _recurse_no_nodes(self, nodes): - return [] - - recurse_nodes = _recurse_no_nodes - - def add_scanner(self, skey, scanner): - self.function[skey] = scanner - self.add_skey(skey) - - -class Selector(Base): - """ - A class for selecting a more specific scanner based on the - scanner_key() (suffix) for a specific Node. - - TODO: This functionality has been moved into the inner workings of - the Base class, and this class will be deprecated at some point. - (It was never exposed directly as part of the public interface, - although it is used by the Scanner() factory function that was - used by various Tool modules and therefore was likely a template - for custom modules that may be out there.) - """ - def __init__(self, dict, *args, **kw): - Base.__init__(self, None, *args, **kw) - self.dict = dict - self.skeys = list(dict.keys()) - - def __call__(self, node, env, path = ()): - return self.select(node)(node, env, path) - - def select(self, node): - try: - return self.dict[node.scanner_key()] - except KeyError: - return None - - def add_scanner(self, skey, scanner): - self.dict[skey] = scanner - self.add_skey(skey) - - -class Current(Base): - """ - A class for scanning files that are source files (have no builder) - or are derived files and are current (which implies that they exist, - either locally or in a repository). - """ - - def __init__(self, *args, **kw): - def current_check(node, env): - return not node.has_builder() or node.is_up_to_date() - kw['scan_check'] = current_check - Base.__init__(self, *args, **kw) - -class Classic(Current): - """ - A Scanner subclass to contain the common logic for classic CPP-style - include scanning, but which can be customized to use different - regular expressions to find the includes. - - Note that in order for this to work "out of the box" (without - overriding the find_include() and sort_key() methods), the regular - expression passed to the constructor must return the name of the - include file in group 0. - """ - - def __init__(self, name, suffixes, path_variable, regex, *args, **kw): - - self.cre = re.compile(regex, re.M) - - def _scan(node, env, path=(), self=self): - node = node.rfile() - if not node.exists(): - return [] - return self.scan(node, path) - - kw['function'] = _scan - kw['path_function'] = FindPathDirs(path_variable) - kw['recursive'] = 1 - kw['skeys'] = suffixes - kw['name'] = name - - Current.__init__(self, *args, **kw) - - def find_include(self, include, source_dir, path): - n = SCons.Node.FS.find_file(include, (source_dir,) + tuple(path)) - return n, include - - def sort_key(self, include): - return SCons.Node.FS._my_normcase(include) - - def find_include_names(self, node): - return self.cre.findall(node.get_text_contents()) - - def scan(self, node, path=()): - - # cache the includes list in node so we only scan it once: - if node.includes is not None: - includes = node.includes - else: - includes = self.find_include_names (node) - # Intern the names of the include files. Saves some memory - # if the same header is included many times. - node.includes = list(map(SCons.Util.silent_intern, includes)) - - # This is a hand-coded DSU (decorate-sort-undecorate, or - # Schwartzian transform) pattern. The sort key is the raw name - # of the file as specifed on the #include line (including the - # " or <, since that may affect what file is found), which lets - # us keep the sort order constant regardless of whether the file - # is actually found in a Repository or locally. - nodes = [] - source_dir = node.get_dir() - if callable(path): - path = path() - for include in includes: - n, i = self.find_include(include, source_dir, path) - - if n is None: - SCons.Warnings.warn(SCons.Warnings.DependencyWarning, - "No dependency generated for file: %s (included from: %s) -- file not found" % (i, node)) - else: - nodes.append((self.sort_key(include), n)) - - return [pair[1] for pair in sorted(nodes)] - -class ClassicCPP(Classic): - """ - A Classic Scanner subclass which takes into account the type of - bracketing used to include the file, and uses classic CPP rules - for searching for the files based on the bracketing. - - Note that in order for this to work, the regular expression passed - to the constructor must return the leading bracket in group 0, and - the contained filename in group 1. - """ - def find_include(self, include, source_dir, path): - if include[0] == '"': - paths = (source_dir,) + tuple(path) - else: - paths = tuple(path) + (source_dir,) - - n = SCons.Node.FS.find_file(include[1], paths) - - i = SCons.Util.silent_intern(include[1]) - return n, i - - def sort_key(self, include): - return SCons.Node.FS._my_normcase(' '.join(include)) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Script/Interactive.py b/scons/scons-local-2.2.0/SCons/Script/Interactive.py deleted file mode 100644 index e0822a7e3..000000000 --- a/scons/scons-local-2.2.0/SCons/Script/Interactive.py +++ /dev/null @@ -1,384 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Script/Interactive.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -__doc__ = """ -SCons interactive mode -""" - -# TODO: -# -# This has the potential to grow into something with a really big life -# of its own, which might or might not be a good thing. Nevertheless, -# here are some enhancements that will probably be requested some day -# and are worth keeping in mind (assuming this takes off): -# -# - A command to re-read / re-load the SConscript files. This may -# involve allowing people to specify command-line options (e.g. -f, -# -I, --no-site-dir) that affect how the SConscript files are read. -# -# - Additional command-line options on the "build" command. -# -# Of the supported options that seemed to make sense (after a quick -# pass through the list), the ones that seemed likely enough to be -# used are listed in the man page and have explicit test scripts. -# -# These had code changed in Script/Main.py to support them, but didn't -# seem likely to be used regularly, so had no test scripts added: -# -# build --diskcheck=* -# build --implicit-cache=* -# build --implicit-deps-changed=* -# build --implicit-deps-unchanged=* -# -# These look like they should "just work" with no changes to the -# existing code, but like those above, look unlikely to be used and -# therefore had no test scripts added: -# -# build --random -# -# These I'm not sure about. They might be useful for individual -# "build" commands, and may even work, but they seem unlikely enough -# that we'll wait until they're requested before spending any time on -# writing test scripts for them, or investigating whether they work. -# -# build -q [??? is there a useful analog to the exit status?] -# build --duplicate= -# build --profile= -# build --max-drift= -# build --warn=* -# build --Y -# -# - Most of the SCons command-line options that the "build" command -# supports should be settable as default options that apply to all -# subsequent "build" commands. Maybe a "set {option}" command that -# maps to "SetOption('{option}')". -# -# - Need something in the 'help' command that prints the -h output. -# -# - A command to run the configure subsystem separately (must see how -# this interacts with the new automake model). -# -# - Command-line completion of target names; maybe even of SCons options? -# Completion is something that's supported by the Python cmd module, -# so this should be doable without too much trouble. -# - -import cmd -import copy -import os -import re -import shlex -import sys - -try: - import readline -except ImportError: - pass - -class SConsInteractiveCmd(cmd.Cmd): - """\ - build [TARGETS] Build the specified TARGETS and their dependencies. - 'b' is a synonym. - clean [TARGETS] Clean (remove) the specified TARGETS and their - dependencies. 'c' is a synonym. - exit Exit SCons interactive mode. - help [COMMAND] Prints help for the specified COMMAND. 'h' and - '?' are synonyms. - shell [COMMANDLINE] Execute COMMANDLINE in a subshell. 'sh' and '!' - are synonyms. - version Prints SCons version information. - """ - - synonyms = { - 'b' : 'build', - 'c' : 'clean', - 'h' : 'help', - 'scons' : 'build', - 'sh' : 'shell', - } - - def __init__(self, **kw): - cmd.Cmd.__init__(self) - for key, val in kw.items(): - setattr(self, key, val) - - if sys.platform == 'win32': - self.shell_variable = 'COMSPEC' - else: - self.shell_variable = 'SHELL' - - def default(self, argv): - print "*** Unknown command: %s" % argv[0] - - def onecmd(self, line): - line = line.strip() - if not line: - print self.lastcmd - return self.emptyline() - self.lastcmd = line - if line[0] == '!': - line = 'shell ' + line[1:] - elif line[0] == '?': - line = 'help ' + line[1:] - if os.sep == '\\': - line = line.replace('\\', '\\\\') - argv = shlex.split(line) - argv[0] = self.synonyms.get(argv[0], argv[0]) - if not argv[0]: - return self.default(line) - else: - try: - func = getattr(self, 'do_' + argv[0]) - except AttributeError: - return self.default(argv) - return func(argv) - - def do_build(self, argv): - """\ - build [TARGETS] Build the specified TARGETS and their - dependencies. 'b' is a synonym. - """ - import SCons.Node - import SCons.SConsign - import SCons.Script.Main - - options = copy.deepcopy(self.options) - - options, targets = self.parser.parse_args(argv[1:], values=options) - - SCons.Script.COMMAND_LINE_TARGETS = targets - - if targets: - SCons.Script.BUILD_TARGETS = targets - else: - # If the user didn't specify any targets on the command line, - # use the list of default targets. - SCons.Script.BUILD_TARGETS = SCons.Script._build_plus_default - - nodes = SCons.Script.Main._build_targets(self.fs, - options, - targets, - self.target_top) - - if not nodes: - return - - # Call each of the Node's alter_targets() methods, which may - # provide additional targets that ended up as part of the build - # (the canonical example being a VariantDir() when we're building - # from a source directory) and which we therefore need their - # state cleared, too. - x = [] - for n in nodes: - x.extend(n.alter_targets()[0]) - nodes.extend(x) - - # Clean up so that we can perform the next build correctly. - # - # We do this by walking over all the children of the targets, - # and clearing their state. - # - # We currently have to re-scan each node to find their - # children, because built nodes have already been partially - # cleared and don't remember their children. (In scons - # 0.96.1 and earlier, this wasn't the case, and we didn't - # have to re-scan the nodes.) - # - # Because we have to re-scan each node, we can't clear the - # nodes as we walk over them, because we may end up rescanning - # a cleared node as we scan a later node. Therefore, only - # store the list of nodes that need to be cleared as we walk - # the tree, and clear them in a separate pass. - # - # XXX: Someone more familiar with the inner workings of scons - # may be able to point out a more efficient way to do this. - - SCons.Script.Main.progress_display("scons: Clearing cached node information ...") - - seen_nodes = {} - - def get_unseen_children(node, parent, seen_nodes=seen_nodes): - def is_unseen(node, seen_nodes=seen_nodes): - return node not in seen_nodes - return list(filter(is_unseen, node.children(scan=1))) - - def add_to_seen_nodes(node, parent, seen_nodes=seen_nodes): - seen_nodes[node] = 1 - - # If this file is in a VariantDir and has a - # corresponding source file in the source tree, remember the - # node in the source tree, too. This is needed in - # particular to clear cached implicit dependencies on the - # source file, since the scanner will scan it if the - # VariantDir was created with duplicate=0. - try: - rfile_method = node.rfile - except AttributeError: - return - else: - rfile = rfile_method() - if rfile != node: - seen_nodes[rfile] = 1 - - for node in nodes: - walker = SCons.Node.Walker(node, - kids_func=get_unseen_children, - eval_func=add_to_seen_nodes) - n = walker.get_next() - while n: - n = walker.get_next() - - for node in seen_nodes.keys(): - # Call node.clear() to clear most of the state - node.clear() - # node.clear() doesn't reset node.state, so call - # node.set_state() to reset it manually - node.set_state(SCons.Node.no_state) - node.implicit = None - - # Debug: Uncomment to verify that all Taskmaster reference - # counts have been reset to zero. - #if node.ref_count != 0: - # from SCons.Debug import Trace - # Trace('node %s, ref_count %s !!!\n' % (node, node.ref_count)) - - SCons.SConsign.Reset() - SCons.Script.Main.progress_display("scons: done clearing node information.") - - def do_clean(self, argv): - """\ - clean [TARGETS] Clean (remove) the specified TARGETS - and their dependencies. 'c' is a synonym. - """ - return self.do_build(['build', '--clean'] + argv[1:]) - - def do_EOF(self, argv): - print - self.do_exit(argv) - - def _do_one_help(self, arg): - try: - # If help_() exists, then call it. - func = getattr(self, 'help_' + arg) - except AttributeError: - try: - func = getattr(self, 'do_' + arg) - except AttributeError: - doc = None - else: - doc = self._doc_to_help(func) - if doc: - sys.stdout.write(doc + '\n') - sys.stdout.flush() - else: - doc = self.strip_initial_spaces(func()) - if doc: - sys.stdout.write(doc + '\n') - sys.stdout.flush() - - def _doc_to_help(self, obj): - doc = obj.__doc__ - if doc is None: - return '' - return self._strip_initial_spaces(doc) - - def _strip_initial_spaces(self, s): - #lines = s.split('\n') - lines = s.split('\n') - spaces = re.match(' *', lines[0]).group(0) - #def strip_spaces(l): - # if l.startswith(spaces): - # l = l[len(spaces):] - # return l - #return '\n'.join([ strip_spaces(l) for l in lines ]) - def strip_spaces(l, spaces=spaces): - if l[:len(spaces)] == spaces: - l = l[len(spaces):] - return l - lines = list(map(strip_spaces, lines)) - return '\n'.join(lines) - - def do_exit(self, argv): - """\ - exit Exit SCons interactive mode. - """ - sys.exit(0) - - def do_help(self, argv): - """\ - help [COMMAND] Prints help for the specified COMMAND. 'h' - and '?' are synonyms. - """ - if argv[1:]: - for arg in argv[1:]: - if self._do_one_help(arg): - break - else: - # If bare 'help' is called, print this class's doc - # string (if it has one). - doc = self._doc_to_help(self.__class__) - if doc: - sys.stdout.write(doc + '\n') - sys.stdout.flush() - - def do_shell(self, argv): - """\ - shell [COMMANDLINE] Execute COMMANDLINE in a subshell. 'sh' and - '!' are synonyms. - """ - import subprocess - argv = argv[1:] - if not argv: - argv = os.environ[self.shell_variable] - try: - # Per "[Python-Dev] subprocess insufficiently platform-independent?" - # http://mail.python.org/pipermail/python-dev/2008-August/081979.html "+ - # Doing the right thing with an argument list currently - # requires different shell= values on Windows and Linux. - p = subprocess.Popen(argv, shell=(sys.platform=='win32')) - except EnvironmentError, e: - sys.stderr.write('scons: %s: %s\n' % (argv[0], e.strerror)) - else: - p.wait() - - def do_version(self, argv): - """\ - version Prints SCons version information. - """ - sys.stdout.write(self.parser.version + '\n') - -def interact(fs, parser, options, targets, target_top): - c = SConsInteractiveCmd(prompt = 'scons>>> ', - fs = fs, - parser = parser, - options = options, - targets = targets, - target_top = target_top) - c.cmdloop() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Script/Main.py b/scons/scons-local-2.2.0/SCons/Script/Main.py deleted file mode 100644 index 12d1bc256..000000000 --- a/scons/scons-local-2.2.0/SCons/Script/Main.py +++ /dev/null @@ -1,1406 +0,0 @@ -"""SCons.Script - -This file implements the main() function used by the scons script. - -Architecturally, this *is* the scons script, and will likely only be -called from the external "scons" wrapper. Consequently, anything here -should not be, or be considered, part of the build engine. If it's -something that we expect other software to want to use, it should go in -some other module. If it's specific to the "scons" script invocation, -it goes here. -""" - -unsupported_python_version = (2, 3, 0) -deprecated_python_version = (2, 4, 0) - -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Script/Main.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.compat - -import os -import sys -import time -import traceback - -# Strip the script directory from sys.path() so on case-insensitive -# (Windows) systems Python doesn't think that the "scons" script is the -# "SCons" package. Replace it with our own version directory so, if -# if they're there, we pick up the right version of the build engine -# modules. -#sys.path = [os.path.join(sys.prefix, -# 'lib', -# 'scons-%d' % SCons.__version__)] + sys.path[1:] - -import SCons.CacheDir -import SCons.Debug -import SCons.Defaults -import SCons.Environment -import SCons.Errors -import SCons.Job -import SCons.Node -import SCons.Node.FS -import SCons.Platform -import SCons.SConf -import SCons.Script -import SCons.Taskmaster -import SCons.Util -import SCons.Warnings - -import SCons.Script.Interactive - -def fetch_win32_parallel_msg(): - # A subsidiary function that exists solely to isolate this import - # so we don't have to pull it in on all platforms, and so that an - # in-line "import" statement in the _main() function below doesn't - # cause warnings about local names shadowing use of the 'SCons' - # globl in nest scopes and UnboundLocalErrors and the like in some - # versions (2.1) of Python. - import SCons.Platform.win32 - return SCons.Platform.win32.parallel_msg - -# - -class SConsPrintHelpException(Exception): - pass - -display = SCons.Util.display -progress_display = SCons.Util.DisplayEngine() - -first_command_start = None -last_command_end = None - -class Progressor(object): - prev = '' - count = 0 - target_string = '$TARGET' - - def __init__(self, obj, interval=1, file=None, overwrite=False): - if file is None: - file = sys.stdout - - self.obj = obj - self.file = file - self.interval = interval - self.overwrite = overwrite - - if callable(obj): - self.func = obj - elif SCons.Util.is_List(obj): - self.func = self.spinner - elif obj.find(self.target_string) != -1: - self.func = self.replace_string - else: - self.func = self.string - - def write(self, s): - self.file.write(s) - self.file.flush() - self.prev = s - - def erase_previous(self): - if self.prev: - length = len(self.prev) - if self.prev[-1] in ('\n', '\r'): - length = length - 1 - self.write(' ' * length + '\r') - self.prev = '' - - def spinner(self, node): - self.write(self.obj[self.count % len(self.obj)]) - - def string(self, node): - self.write(self.obj) - - def replace_string(self, node): - self.write(self.obj.replace(self.target_string, str(node))) - - def __call__(self, node): - self.count = self.count + 1 - if (self.count % self.interval) == 0: - if self.overwrite: - self.erase_previous() - self.func(node) - -ProgressObject = SCons.Util.Null() - -def Progress(*args, **kw): - global ProgressObject - ProgressObject = Progressor(*args, **kw) - -# Task control. -# - -_BuildFailures = [] - -def GetBuildFailures(): - return _BuildFailures - -class BuildTask(SCons.Taskmaster.OutOfDateTask): - """An SCons build task.""" - progress = ProgressObject - - def display(self, message): - display('scons: ' + message) - - def prepare(self): - self.progress(self.targets[0]) - return SCons.Taskmaster.OutOfDateTask.prepare(self) - - def needs_execute(self): - if SCons.Taskmaster.OutOfDateTask.needs_execute(self): - return True - if self.top and self.targets[0].has_builder(): - display("scons: `%s' is up to date." % str(self.node)) - return False - - def execute(self): - if print_time: - start_time = time.time() - global first_command_start - if first_command_start is None: - first_command_start = start_time - SCons.Taskmaster.OutOfDateTask.execute(self) - if print_time: - global cumulative_command_time - global last_command_end - finish_time = time.time() - last_command_end = finish_time - cumulative_command_time = cumulative_command_time+finish_time-start_time - sys.stdout.write("Command execution time: %f seconds\n"%(finish_time-start_time)) - - def do_failed(self, status=2): - _BuildFailures.append(self.exception[1]) - global exit_status - global this_build_status - if self.options.ignore_errors: - SCons.Taskmaster.OutOfDateTask.executed(self) - elif self.options.keep_going: - SCons.Taskmaster.OutOfDateTask.fail_continue(self) - exit_status = status - this_build_status = status - else: - SCons.Taskmaster.OutOfDateTask.fail_stop(self) - exit_status = status - this_build_status = status - - def executed(self): - t = self.targets[0] - if self.top and not t.has_builder() and not t.side_effect: - if not t.exists(): - if t.__class__.__name__ in ('File', 'Dir', 'Entry'): - errstr="Do not know how to make %s target `%s' (%s)." % (t.__class__.__name__, t, t.abspath) - else: # Alias or Python or ... - errstr="Do not know how to make %s target `%s'." % (t.__class__.__name__, t) - sys.stderr.write("scons: *** " + errstr) - if not self.options.keep_going: - sys.stderr.write(" Stop.") - sys.stderr.write("\n") - try: - raise SCons.Errors.BuildError(t, errstr) - except KeyboardInterrupt: - raise - except: - self.exception_set() - self.do_failed() - else: - print "scons: Nothing to be done for `%s'." % t - SCons.Taskmaster.OutOfDateTask.executed(self) - else: - SCons.Taskmaster.OutOfDateTask.executed(self) - - def failed(self): - # Handle the failure of a build task. The primary purpose here - # is to display the various types of Errors and Exceptions - # appropriately. - exc_info = self.exc_info() - try: - t, e, tb = exc_info - except ValueError: - t, e = exc_info - tb = None - - if t is None: - # The Taskmaster didn't record an exception for this Task; - # see if the sys module has one. - try: - t, e, tb = sys.exc_info()[:] - except ValueError: - t, e = exc_info - tb = None - - # Deprecated string exceptions will have their string stored - # in the first entry of the tuple. - if e is None: - e = t - - buildError = SCons.Errors.convert_to_BuildError(e) - if not buildError.node: - buildError.node = self.node - - node = buildError.node - if not SCons.Util.is_List(node): - node = [ node ] - nodename = ', '.join(map(str, node)) - - errfmt = "scons: *** [%s] %s\n" - sys.stderr.write(errfmt % (nodename, buildError)) - - if (buildError.exc_info[2] and buildError.exc_info[1] and - not isinstance( - buildError.exc_info[1], - (EnvironmentError, SCons.Errors.StopError, - SCons.Errors.UserError))): - type, value, trace = buildError.exc_info - traceback.print_exception(type, value, trace) - elif tb and print_stacktrace: - sys.stderr.write("scons: internal stack trace:\n") - traceback.print_tb(tb, file=sys.stderr) - - self.exception = (e, buildError, tb) # type, value, traceback - self.do_failed(buildError.exitstatus) - - self.exc_clear() - - def postprocess(self): - if self.top: - t = self.targets[0] - for tp in self.options.tree_printers: - tp.display(t) - if self.options.debug_includes: - tree = t.render_include_tree() - if tree: - print - print tree - SCons.Taskmaster.OutOfDateTask.postprocess(self) - - def make_ready(self): - """Make a task ready for execution""" - SCons.Taskmaster.OutOfDateTask.make_ready(self) - if self.out_of_date and self.options.debug_explain: - explanation = self.out_of_date[0].explain() - if explanation: - sys.stdout.write("scons: " + explanation) - -class CleanTask(SCons.Taskmaster.AlwaysTask): - """An SCons clean task.""" - def fs_delete(self, path, pathstr, remove=1): - try: - if os.path.lexists(path): - if os.path.isfile(path) or os.path.islink(path): - if remove: os.unlink(path) - display("Removed " + pathstr) - elif os.path.isdir(path) and not os.path.islink(path): - # delete everything in the dir - for e in sorted(os.listdir(path)): - p = os.path.join(path, e) - s = os.path.join(pathstr, e) - if os.path.isfile(p): - if remove: os.unlink(p) - display("Removed " + s) - else: - self.fs_delete(p, s, remove) - # then delete dir itself - if remove: os.rmdir(path) - display("Removed directory " + pathstr) - else: - errstr = "Path '%s' exists but isn't a file or directory." - raise SCons.Errors.UserError(errstr % (pathstr)) - except SCons.Errors.UserError, e: - print e - except (IOError, OSError), e: - print "scons: Could not remove '%s':" % pathstr, e.strerror - - def show(self): - target = self.targets[0] - if (target.has_builder() or target.side_effect) and not target.noclean: - for t in self.targets: - if not t.isdir(): - display("Removed " + str(t)) - if target in SCons.Environment.CleanTargets: - files = SCons.Environment.CleanTargets[target] - for f in files: - self.fs_delete(f.abspath, str(f), 0) - - def remove(self): - target = self.targets[0] - if (target.has_builder() or target.side_effect) and not target.noclean: - for t in self.targets: - try: - removed = t.remove() - except OSError, e: - # An OSError may indicate something like a permissions - # issue, an IOError would indicate something like - # the file not existing. In either case, print a - # message and keep going to try to remove as many - # targets aa possible. - print "scons: Could not remove '%s':" % str(t), e.strerror - else: - if removed: - display("Removed " + str(t)) - if target in SCons.Environment.CleanTargets: - files = SCons.Environment.CleanTargets[target] - for f in files: - self.fs_delete(f.abspath, str(f)) - - execute = remove - - # We want the Taskmaster to update the Node states (and therefore - # handle reference counts, etc.), but we don't want to call - # back to the Node's post-build methods, which would do things - # we don't want, like store .sconsign information. - executed = SCons.Taskmaster.Task.executed_without_callbacks - - # Have the taskmaster arrange to "execute" all of the targets, because - # we'll figure out ourselves (in remove() or show() above) whether - # anything really needs to be done. - make_ready = SCons.Taskmaster.Task.make_ready_all - - def prepare(self): - pass - -class QuestionTask(SCons.Taskmaster.AlwaysTask): - """An SCons task for the -q (question) option.""" - def prepare(self): - pass - - def execute(self): - if self.targets[0].get_state() != SCons.Node.up_to_date or \ - (self.top and not self.targets[0].exists()): - global exit_status - global this_build_status - exit_status = 1 - this_build_status = 1 - self.tm.stop() - - def executed(self): - pass - - -class TreePrinter(object): - def __init__(self, derived=False, prune=False, status=False): - self.derived = derived - self.prune = prune - self.status = status - def get_all_children(self, node): - return node.all_children() - def get_derived_children(self, node): - children = node.all_children(None) - return [x for x in children if x.has_builder()] - def display(self, t): - if self.derived: - func = self.get_derived_children - else: - func = self.get_all_children - s = self.status and 2 or 0 - SCons.Util.print_tree(t, func, prune=self.prune, showtags=s) - - -def python_version_string(): - return sys.version.split()[0] - -def python_version_unsupported(version=sys.version_info): - return version < unsupported_python_version - -def python_version_deprecated(version=sys.version_info): - return version < deprecated_python_version - - -# Global variables - -print_objects = 0 -print_memoizer = 0 -print_stacktrace = 0 -print_time = 0 -sconscript_time = 0 -cumulative_command_time = 0 -exit_status = 0 # final exit status, assume success by default -this_build_status = 0 # "exit status" of an individual build -num_jobs = None -delayed_warnings = [] - -class FakeOptionParser(object): - """ - A do-nothing option parser, used for the initial OptionsParser variable. - - During normal SCons operation, the OptionsParser is created right - away by the main() function. Certain tests scripts however, can - introspect on different Tool modules, the initialization of which - can try to add a new, local option to an otherwise uninitialized - OptionsParser object. This allows that introspection to happen - without blowing up. - - """ - class FakeOptionValues(object): - def __getattr__(self, attr): - return None - values = FakeOptionValues() - def add_local_option(self, *args, **kw): - pass - -OptionsParser = FakeOptionParser() - -def AddOption(*args, **kw): - if 'default' not in kw: - kw['default'] = None - result = OptionsParser.add_local_option(*args, **kw) - return result - -def GetOption(name): - return getattr(OptionsParser.values, name) - -def SetOption(name, value): - return OptionsParser.values.set_option(name, value) - -# -class Stats(object): - def __init__(self): - self.stats = [] - self.labels = [] - self.append = self.do_nothing - self.print_stats = self.do_nothing - def enable(self, outfp): - self.outfp = outfp - self.append = self.do_append - self.print_stats = self.do_print - def do_nothing(self, *args, **kw): - pass - -class CountStats(Stats): - def do_append(self, label): - self.labels.append(label) - self.stats.append(SCons.Debug.fetchLoggedInstances()) - def do_print(self): - stats_table = {} - for s in self.stats: - for n in [t[0] for t in s]: - stats_table[n] = [0, 0, 0, 0] - i = 0 - for s in self.stats: - for n, c in s: - stats_table[n][i] = c - i = i + 1 - self.outfp.write("Object counts:\n") - pre = [" "] - post = [" %s\n"] - l = len(self.stats) - fmt1 = ''.join(pre + [' %7s']*l + post) - fmt2 = ''.join(pre + [' %7d']*l + post) - labels = self.labels[:l] - labels.append(("", "Class")) - self.outfp.write(fmt1 % tuple([x[0] for x in labels])) - self.outfp.write(fmt1 % tuple([x[1] for x in labels])) - for k in sorted(stats_table.keys()): - r = stats_table[k][:l] + [k] - self.outfp.write(fmt2 % tuple(r)) - -count_stats = CountStats() - -class MemStats(Stats): - def do_append(self, label): - self.labels.append(label) - self.stats.append(SCons.Debug.memory()) - def do_print(self): - fmt = 'Memory %-32s %12d\n' - for label, stats in zip(self.labels, self.stats): - self.outfp.write(fmt % (label, stats)) - -memory_stats = MemStats() - -# utility functions - -def _scons_syntax_error(e): - """Handle syntax errors. Print out a message and show where the error - occurred. - """ - etype, value, tb = sys.exc_info() - lines = traceback.format_exception_only(etype, value) - for line in lines: - sys.stderr.write(line+'\n') - sys.exit(2) - -def find_deepest_user_frame(tb): - """ - Find the deepest stack frame that is not part of SCons. - - Input is a "pre-processed" stack trace in the form - returned by traceback.extract_tb() or traceback.extract_stack() - """ - - tb.reverse() - - # find the deepest traceback frame that is not part - # of SCons: - for frame in tb: - filename = frame[0] - if filename.find(os.sep+'SCons'+os.sep) == -1: - return frame - return tb[0] - -def _scons_user_error(e): - """Handle user errors. Print out a message and a description of the - error, along with the line number and routine where it occured. - The file and line number will be the deepest stack frame that is - not part of SCons itself. - """ - global print_stacktrace - etype, value, tb = sys.exc_info() - if print_stacktrace: - traceback.print_exception(etype, value, tb) - filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb)) - sys.stderr.write("\nscons: *** %s\n" % value) - sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine)) - sys.exit(2) - -def _scons_user_warning(e): - """Handle user warnings. Print out a message and a description of - the warning, along with the line number and routine where it occured. - The file and line number will be the deepest stack frame that is - not part of SCons itself. - """ - etype, value, tb = sys.exc_info() - filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb)) - sys.stderr.write("\nscons: warning: %s\n" % e) - sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine)) - -def _scons_internal_warning(e): - """Slightly different from _scons_user_warning in that we use the - *current call stack* rather than sys.exc_info() to get our stack trace. - This is used by the warnings framework to print warnings.""" - filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_stack()) - sys.stderr.write("\nscons: warning: %s\n" % e.args[0]) - sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine)) - -def _scons_internal_error(): - """Handle all errors but user errors. Print out a message telling - the user what to do in this case and print a normal trace. - """ - print 'internal error' - traceback.print_exc() - sys.exit(2) - -def _SConstruct_exists(dirname='', repositories=[], filelist=None): - """This function checks that an SConstruct file exists in a directory. - If so, it returns the path of the file. By default, it checks the - current directory. - """ - if not filelist: - filelist = ['SConstruct', 'Sconstruct', 'sconstruct'] - for file in filelist: - sfile = os.path.join(dirname, file) - if os.path.isfile(sfile): - return sfile - if not os.path.isabs(sfile): - for rep in repositories: - if os.path.isfile(os.path.join(rep, sfile)): - return sfile - return None - -def _set_debug_values(options): - global print_memoizer, print_objects, print_stacktrace, print_time - - debug_values = options.debug - - if "count" in debug_values: - # All of the object counts are within "if __debug__:" blocks, - # which get stripped when running optimized (with python -O or - # from compiled *.pyo files). Provide a warning if __debug__ is - # stripped, so it doesn't just look like --debug=count is broken. - enable_count = False - if __debug__: enable_count = True - if enable_count: - count_stats.enable(sys.stdout) - else: - msg = "--debug=count is not supported when running SCons\n" + \ - "\twith the python -O option or optimized (.pyo) modules." - SCons.Warnings.warn(SCons.Warnings.NoObjectCountWarning, msg) - if "dtree" in debug_values: - options.tree_printers.append(TreePrinter(derived=True)) - options.debug_explain = ("explain" in debug_values) - if "findlibs" in debug_values: - SCons.Scanner.Prog.print_find_libs = "findlibs" - options.debug_includes = ("includes" in debug_values) - print_memoizer = ("memoizer" in debug_values) - if "memory" in debug_values: - memory_stats.enable(sys.stdout) - print_objects = ("objects" in debug_values) - if "presub" in debug_values: - SCons.Action.print_actions_presub = 1 - if "stacktrace" in debug_values: - print_stacktrace = 1 - if "stree" in debug_values: - options.tree_printers.append(TreePrinter(status=True)) - if "time" in debug_values: - print_time = 1 - if "tree" in debug_values: - options.tree_printers.append(TreePrinter()) - if "prepare" in debug_values: - SCons.Taskmaster.print_prepare = 1 - if "duplicate" in debug_values: - SCons.Node.FS.print_duplicate = 1 - -def _create_path(plist): - path = '.' - for d in plist: - if os.path.isabs(d): - path = d - else: - path = path + '/' + d - return path - -def _load_site_scons_dir(topdir, site_dir_name=None): - """Load the site_scons dir under topdir. - Prepends site_scons to sys.path, imports site_scons/site_init.py, - and prepends site_scons/site_tools to default toolpath.""" - if site_dir_name: - err_if_not_found = True # user specified: err if missing - else: - site_dir_name = "site_scons" - err_if_not_found = False - - site_dir = os.path.join(topdir, site_dir_name) - if not os.path.exists(site_dir): - if err_if_not_found: - raise SCons.Errors.UserError("site dir %s not found."%site_dir) - return - - site_init_filename = "site_init.py" - site_init_modname = "site_init" - site_tools_dirname = "site_tools" - # prepend to sys.path - sys.path = [os.path.abspath(site_dir)] + sys.path - site_init_file = os.path.join(site_dir, site_init_filename) - site_tools_dir = os.path.join(site_dir, site_tools_dirname) - if os.path.exists(site_init_file): - import imp, re - # TODO(2.4): turn this into try:-except:-finally: - try: - try: - fp, pathname, description = imp.find_module(site_init_modname, - [site_dir]) - # Load the file into SCons.Script namespace. This is - # opaque and clever; m is the module object for the - # SCons.Script module, and the exec ... in call executes a - # file (or string containing code) in the context of the - # module's dictionary, so anything that code defines ends - # up adding to that module. This is really short, but all - # the error checking makes it longer. - try: - m = sys.modules['SCons.Script'] - except Exception, e: - fmt = 'cannot import site_init.py: missing SCons.Script module %s' - raise SCons.Errors.InternalError(fmt % repr(e)) - try: - sfx = description[0] - modname = os.path.basename(pathname)[:-len(sfx)] - site_m = {"__file__": pathname, "__name__": modname, "__doc__": None} - re_special = re.compile("__[^_]+__") - for k in m.__dict__.keys(): - if not re_special.match(k): - site_m[k] = m.__dict__[k] - - # This is the magic. - exec fp in site_m - except KeyboardInterrupt: - raise - except Exception, e: - fmt = '*** Error loading site_init file %s:\n' - sys.stderr.write(fmt % repr(site_init_file)) - raise - else: - for k in site_m: - if not re_special.match(k): - m.__dict__[k] = site_m[k] - except KeyboardInterrupt: - raise - except ImportError, e: - fmt = '*** cannot import site init file %s:\n' - sys.stderr.write(fmt % repr(site_init_file)) - raise - finally: - if fp: - fp.close() - if os.path.exists(site_tools_dir): - # prepend to DefaultToolpath - SCons.Tool.DefaultToolpath.insert(0, os.path.abspath(site_tools_dir)) - -def _load_all_site_scons_dirs(topdir, verbose=None): - """Load all of the predefined site_scons dir. - Order is significant; we load them in order from most generic - (machine-wide) to most specific (topdir). - The verbose argument is only for testing. - """ - platform = SCons.Platform.platform_default() - - def homedir(d): - return os.path.expanduser('~/'+d) - - if platform == 'win32' or platform == 'cygwin': - # Note we use $ here instead of %...% because older - # pythons (prior to 2.6?) didn't expand %...% on Windows. - # This set of dirs should work on XP, Vista, 7 and later. - sysdirs=[ - os.path.expandvars('$ALLUSERSPROFILE\\Application Data\\scons'), - os.path.expandvars('$USERPROFILE\\Local Settings\\Application Data\\scons')] - appdatadir = os.path.expandvars('$APPDATA\\scons') - if appdatadir not in sysdirs: - sysdirs.append(appdatadir) - sysdirs.append(homedir('.scons')) - - elif platform == 'darwin': # MacOS X - sysdirs=['/Library/Application Support/SCons', - '/opt/local/share/scons', # (for MacPorts) - '/sw/share/scons', # (for Fink) - homedir('Library/Application Support/SCons'), - homedir('.scons')] - elif platform == 'sunos': # Solaris - sysdirs=['/opt/sfw/scons', - '/usr/share/scons', - homedir('.scons')] - else: # Linux, HPUX, etc. - # assume posix-like, i.e. platform == 'posix' - sysdirs=['/usr/share/scons', - homedir('.scons')] - - dirs=sysdirs + [topdir] - for d in dirs: - if verbose: # this is used by unit tests. - print "Loading site dir ", d - _load_site_scons_dir(d) - -def test_load_all_site_scons_dirs(d): - _load_all_site_scons_dirs(d, True) - -def version_string(label, module): - version = module.__version__ - build = module.__build__ - if build: - if build[0] != '.': - build = '.' + build - version = version + build - fmt = "\t%s: v%s, %s, by %s on %s\n" - return fmt % (label, - version, - module.__date__, - module.__developer__, - module.__buildsys__) - -def path_string(label, module): - path = module.__path__ - return "\t%s path: %s\n"%(label,path) - -def _main(parser): - global exit_status - global this_build_status - - options = parser.values - - # Here's where everything really happens. - - # First order of business: set up default warnings and then - # handle the user's warning options, so that we can issue (or - # suppress) appropriate warnings about anything that might happen, - # as configured by the user. - - default_warnings = [ SCons.Warnings.WarningOnByDefault, - SCons.Warnings.DeprecatedWarning, - ] - - for warning in default_warnings: - SCons.Warnings.enableWarningClass(warning) - SCons.Warnings._warningOut = _scons_internal_warning - SCons.Warnings.process_warn_strings(options.warn) - - # Now that we have the warnings configuration set up, we can actually - # issue (or suppress) any warnings about warning-worthy things that - # occurred while the command-line options were getting parsed. - try: - dw = options.delayed_warnings - except AttributeError: - pass - else: - delayed_warnings.extend(dw) - for warning_type, message in delayed_warnings: - SCons.Warnings.warn(warning_type, message) - - if options.diskcheck: - SCons.Node.FS.set_diskcheck(options.diskcheck) - - # Next, we want to create the FS object that represents the outside - # world's file system, as that's central to a lot of initialization. - # To do this, however, we need to be in the directory from which we - # want to start everything, which means first handling any relevant - # options that might cause us to chdir somewhere (-C, -D, -U, -u). - if options.directory: - script_dir = os.path.abspath(_create_path(options.directory)) - else: - script_dir = os.getcwd() - - target_top = None - if options.climb_up: - target_top = '.' # directory to prepend to targets - while script_dir and not _SConstruct_exists(script_dir, - options.repository, - options.file): - script_dir, last_part = os.path.split(script_dir) - if last_part: - target_top = os.path.join(last_part, target_top) - else: - script_dir = '' - - if script_dir and script_dir != os.getcwd(): - if not options.silent: - display("scons: Entering directory `%s'" % script_dir) - try: - os.chdir(script_dir) - except OSError: - sys.stderr.write("Could not change directory to %s\n" % script_dir) - - # Now that we're in the top-level SConstruct directory, go ahead - # and initialize the FS object that represents the file system, - # and make it the build engine default. - fs = SCons.Node.FS.get_default_fs() - - for rep in options.repository: - fs.Repository(rep) - - # Now that we have the FS object, the next order of business is to - # check for an SConstruct file (or other specified config file). - # If there isn't one, we can bail before doing any more work. - scripts = [] - if options.file: - scripts.extend(options.file) - if not scripts: - sfile = _SConstruct_exists(repositories=options.repository, - filelist=options.file) - if sfile: - scripts.append(sfile) - - if not scripts: - if options.help: - # There's no SConstruct, but they specified -h. - # Give them the options usage now, before we fail - # trying to read a non-existent SConstruct file. - raise SConsPrintHelpException - raise SCons.Errors.UserError("No SConstruct file found.") - - if scripts[0] == "-": - d = fs.getcwd() - else: - d = fs.File(scripts[0]).dir - fs.set_SConstruct_dir(d) - - _set_debug_values(options) - SCons.Node.implicit_cache = options.implicit_cache - SCons.Node.implicit_deps_changed = options.implicit_deps_changed - SCons.Node.implicit_deps_unchanged = options.implicit_deps_unchanged - - if options.no_exec: - SCons.SConf.dryrun = 1 - SCons.Action.execute_actions = None - if options.question: - SCons.SConf.dryrun = 1 - if options.clean: - SCons.SConf.SetBuildType('clean') - if options.help: - SCons.SConf.SetBuildType('help') - SCons.SConf.SetCacheMode(options.config) - SCons.SConf.SetProgressDisplay(progress_display) - - if options.no_progress or options.silent: - progress_display.set_mode(0) - - if options.site_dir: - _load_site_scons_dir(d.path, options.site_dir) - elif not options.no_site_dir: - _load_all_site_scons_dirs(d.path) - - if options.include_dir: - sys.path = options.include_dir + sys.path - - # That should cover (most of) the options. Next, set up the variables - # that hold command-line arguments, so the SConscript files that we - # read and execute have access to them. - targets = [] - xmit_args = [] - for a in parser.largs: - if a[:1] == '-': - continue - if '=' in a: - xmit_args.append(a) - else: - targets.append(a) - SCons.Script._Add_Targets(targets + parser.rargs) - SCons.Script._Add_Arguments(xmit_args) - - # If stdout is not a tty, replace it with a wrapper object to call flush - # after every write. - # - # Tty devices automatically flush after every newline, so the replacement - # isn't necessary. Furthermore, if we replace sys.stdout, the readline - # module will no longer work. This affects the behavior during - # --interactive mode. --interactive should only be used when stdin and - # stdout refer to a tty. - if not hasattr(sys.stdout, 'isatty') or not sys.stdout.isatty(): - sys.stdout = SCons.Util.Unbuffered(sys.stdout) - if not hasattr(sys.stderr, 'isatty') or not sys.stderr.isatty(): - sys.stderr = SCons.Util.Unbuffered(sys.stderr) - - memory_stats.append('before reading SConscript files:') - count_stats.append(('pre-', 'read')) - - # And here's where we (finally) read the SConscript files. - - progress_display("scons: Reading SConscript files ...") - - start_time = time.time() - try: - for script in scripts: - SCons.Script._SConscript._SConscript(fs, script) - except SCons.Errors.StopError, e: - # We had problems reading an SConscript file, such as it - # couldn't be copied in to the VariantDir. Since we're just - # reading SConscript files and haven't started building - # things yet, stop regardless of whether they used -i or -k - # or anything else. - sys.stderr.write("scons: *** %s Stop.\n" % e) - exit_status = 2 - sys.exit(exit_status) - global sconscript_time - sconscript_time = time.time() - start_time - - progress_display("scons: done reading SConscript files.") - - memory_stats.append('after reading SConscript files:') - count_stats.append(('post-', 'read')) - - # Re-{enable,disable} warnings in case they disabled some in - # the SConscript file. - # - # We delay enabling the PythonVersionWarning class until here so that, - # if they explicity disabled it in either in the command line or in - # $SCONSFLAGS, or in the SConscript file, then the search through - # the list of deprecated warning classes will find that disabling - # first and not issue the warning. - #SCons.Warnings.enableWarningClass(SCons.Warnings.PythonVersionWarning) - SCons.Warnings.process_warn_strings(options.warn) - - # Now that we've read the SConscript files, we can check for the - # warning about deprecated Python versions--delayed until here - # in case they disabled the warning in the SConscript files. - if python_version_deprecated(): - msg = "Support for pre-2.4 Python (%s) is deprecated.\n" + \ - " If this will cause hardship, contact dev@scons.tigris.org." - SCons.Warnings.warn(SCons.Warnings.PythonVersionWarning, - msg % python_version_string()) - - if not options.help: - SCons.SConf.CreateConfigHBuilder(SCons.Defaults.DefaultEnvironment()) - - # Now re-parse the command-line options (any to the left of a '--' - # argument, that is) with any user-defined command-line options that - # the SConscript files may have added to the parser object. This will - # emit the appropriate error message and exit if any unknown option - # was specified on the command line. - - parser.preserve_unknown_options = False - parser.parse_args(parser.largs, options) - - if options.help: - help_text = SCons.Script.help_text - if help_text is None: - # They specified -h, but there was no Help() inside the - # SConscript files. Give them the options usage. - raise SConsPrintHelpException - else: - print help_text - print "Use scons -H for help about command-line options." - exit_status = 0 - return - - # Change directory to the top-level SConstruct directory, then tell - # the Node.FS subsystem that we're all done reading the SConscript - # files and calling Repository() and VariantDir() and changing - # directories and the like, so it can go ahead and start memoizing - # the string values of file system nodes. - - fs.chdir(fs.Top) - - SCons.Node.FS.save_strings(1) - - # Now that we've read the SConscripts we can set the options - # that are SConscript settable: - SCons.Node.implicit_cache = options.implicit_cache - SCons.Node.FS.set_duplicate(options.duplicate) - fs.set_max_drift(options.max_drift) - - SCons.Job.explicit_stack_size = options.stack_size - - if options.md5_chunksize: - SCons.Node.FS.File.md5_chunksize = options.md5_chunksize - - platform = SCons.Platform.platform_module() - - if options.interactive: - SCons.Script.Interactive.interact(fs, OptionsParser, options, - targets, target_top) - - else: - - # Build the targets - nodes = _build_targets(fs, options, targets, target_top) - if not nodes: - exit_status = 2 - -def _build_targets(fs, options, targets, target_top): - - global this_build_status - this_build_status = 0 - - progress_display.set_mode(not (options.no_progress or options.silent)) - display.set_mode(not options.silent) - SCons.Action.print_actions = not options.silent - SCons.Action.execute_actions = not options.no_exec - SCons.Node.FS.do_store_info = not options.no_exec - SCons.SConf.dryrun = options.no_exec - - if options.diskcheck: - SCons.Node.FS.set_diskcheck(options.diskcheck) - - SCons.CacheDir.cache_enabled = not options.cache_disable - SCons.CacheDir.cache_debug = options.cache_debug - SCons.CacheDir.cache_force = options.cache_force - SCons.CacheDir.cache_show = options.cache_show - - if options.no_exec: - CleanTask.execute = CleanTask.show - else: - CleanTask.execute = CleanTask.remove - - lookup_top = None - if targets or SCons.Script.BUILD_TARGETS != SCons.Script._build_plus_default: - # They specified targets on the command line or modified - # BUILD_TARGETS in the SConscript file(s), so if they used -u, - # -U or -D, we have to look up targets relative to the top, - # but we build whatever they specified. - if target_top: - lookup_top = fs.Dir(target_top) - target_top = None - - targets = SCons.Script.BUILD_TARGETS - else: - # There are no targets specified on the command line, - # so if they used -u, -U or -D, we may have to restrict - # what actually gets built. - d = None - if target_top: - if options.climb_up == 1: - # -u, local directory and below - target_top = fs.Dir(target_top) - lookup_top = target_top - elif options.climb_up == 2: - # -D, all Default() targets - target_top = None - lookup_top = None - elif options.climb_up == 3: - # -U, local SConscript Default() targets - target_top = fs.Dir(target_top) - def check_dir(x, target_top=target_top): - if hasattr(x, 'cwd') and not x.cwd is None: - cwd = x.cwd.srcnode() - return cwd == target_top - else: - # x doesn't have a cwd, so it's either not a target, - # or not a file, so go ahead and keep it as a default - # target and let the engine sort it out: - return 1 - d = list(filter(check_dir, SCons.Script.DEFAULT_TARGETS)) - SCons.Script.DEFAULT_TARGETS[:] = d - target_top = None - lookup_top = None - - targets = SCons.Script._Get_Default_Targets(d, fs) - - if not targets: - sys.stderr.write("scons: *** No targets specified and no Default() targets found. Stop.\n") - return None - - def Entry(x, ltop=lookup_top, ttop=target_top, fs=fs): - if isinstance(x, SCons.Node.Node): - node = x - else: - node = None - # Why would ltop be None? Unfortunately this happens. - if ltop is None: ltop = '' - # Curdir becomes important when SCons is called with -u, -C, - # or similar option that changes directory, and so the paths - # of targets given on the command line need to be adjusted. - curdir = os.path.join(os.getcwd(), str(ltop)) - for lookup in SCons.Node.arg2nodes_lookups: - node = lookup(x, curdir=curdir) - if node is not None: - break - if node is None: - node = fs.Entry(x, directory=ltop, create=1) - if ttop and not node.is_under(ttop): - if isinstance(node, SCons.Node.FS.Dir) and ttop.is_under(node): - node = ttop - else: - node = None - return node - - nodes = [_f for _f in map(Entry, targets) if _f] - - task_class = BuildTask # default action is to build targets - opening_message = "Building targets ..." - closing_message = "done building targets." - if options.keep_going: - failure_message = "done building targets (errors occurred during build)." - else: - failure_message = "building terminated because of errors." - if options.question: - task_class = QuestionTask - try: - if options.clean: - task_class = CleanTask - opening_message = "Cleaning targets ..." - closing_message = "done cleaning targets." - if options.keep_going: - failure_message = "done cleaning targets (errors occurred during clean)." - else: - failure_message = "cleaning terminated because of errors." - except AttributeError: - pass - - task_class.progress = ProgressObject - - if options.random: - def order(dependencies): - """Randomize the dependencies.""" - import random - # This is cribbed from the implementation of - # random.shuffle() in Python 2.X. - d = dependencies - for i in range(len(d)-1, 0, -1): - j = int(random.random() * (i+1)) - d[i], d[j] = d[j], d[i] - return d - else: - def order(dependencies): - """Leave the order of dependencies alone.""" - return dependencies - - if options.taskmastertrace_file == '-': - tmtrace = sys.stdout - elif options.taskmastertrace_file: - tmtrace = open(options.taskmastertrace_file, 'wb') - else: - tmtrace = None - taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, order, tmtrace) - - # Let the BuildTask objects get at the options to respond to the - # various print_* settings, tree_printer list, etc. - BuildTask.options = options - - global num_jobs - num_jobs = options.num_jobs - jobs = SCons.Job.Jobs(num_jobs, taskmaster) - if num_jobs > 1: - msg = None - if jobs.num_jobs == 1: - msg = "parallel builds are unsupported by this version of Python;\n" + \ - "\tignoring -j or num_jobs option.\n" - elif sys.platform == 'win32': - msg = fetch_win32_parallel_msg() - if msg: - SCons.Warnings.warn(SCons.Warnings.NoParallelSupportWarning, msg) - - memory_stats.append('before building targets:') - count_stats.append(('pre-', 'build')) - - def jobs_postfunc( - jobs=jobs, - options=options, - closing_message=closing_message, - failure_message=failure_message - ): - if jobs.were_interrupted(): - if not options.no_progress and not options.silent: - sys.stderr.write("scons: Build interrupted.\n") - global exit_status - global this_build_status - exit_status = 2 - this_build_status = 2 - - if this_build_status: - progress_display("scons: " + failure_message) - else: - progress_display("scons: " + closing_message) - if not options.no_exec: - if jobs.were_interrupted(): - progress_display("scons: writing .sconsign file.") - SCons.SConsign.write() - - progress_display("scons: " + opening_message) - jobs.run(postfunc = jobs_postfunc) - - memory_stats.append('after building targets:') - count_stats.append(('post-', 'build')) - - return nodes - -def _exec_main(parser, values): - sconsflags = os.environ.get('SCONSFLAGS', '') - all_args = sconsflags.split() + sys.argv[1:] - - options, args = parser.parse_args(all_args, values) - - if isinstance(options.debug, list) and "pdb" in options.debug: - import pdb - pdb.Pdb().runcall(_main, parser) - elif options.profile_file: - # compat layer imports "cProfile" for us if it's available. - from profile import Profile - - # Some versions of Python 2.4 shipped a profiler that had the - # wrong 'c_exception' entry in its dispatch table. Make sure - # we have the right one. (This may put an unnecessary entry - # in the table in earlier versions of Python, but its presence - # shouldn't hurt anything). - try: - dispatch = Profile.dispatch - except AttributeError: - pass - else: - dispatch['c_exception'] = Profile.trace_dispatch_return - - prof = Profile() - try: - prof.runcall(_main, parser) - except SConsPrintHelpException, e: - prof.dump_stats(options.profile_file) - raise e - except SystemExit: - pass - prof.dump_stats(options.profile_file) - else: - _main(parser) - -def main(): - global OptionsParser - global exit_status - global first_command_start - - # Check up front for a Python version we do not support. We - # delay the check for deprecated Python versions until later, - # after the SConscript files have been read, in case they - # disable that warning. - if python_version_unsupported(): - msg = "scons: *** SCons version %s does not run under Python version %s.\n" - sys.stderr.write(msg % (SCons.__version__, python_version_string())) - sys.exit(1) - - parts = ["SCons by Steven Knight et al.:\n"] - try: - import __main__ - parts.append(version_string("script", __main__)) - except (ImportError, AttributeError): - # On Windows there is no scons.py, so there is no - # __main__.__version__, hence there is no script version. - pass - parts.append(version_string("engine", SCons)) - parts.append(path_string("engine", SCons)) - parts.append("Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation") - version = ''.join(parts) - - import SConsOptions - parser = SConsOptions.Parser(version) - values = SConsOptions.SConsValues(parser.get_default_values()) - - OptionsParser = parser - - try: - _exec_main(parser, values) - except SystemExit, s: - if s: - exit_status = s - except KeyboardInterrupt: - print("scons: Build interrupted.") - sys.exit(2) - except SyntaxError, e: - _scons_syntax_error(e) - except SCons.Errors.InternalError: - _scons_internal_error() - except SCons.Errors.UserError, e: - _scons_user_error(e) - except SConsPrintHelpException: - parser.print_help() - exit_status = 0 - except SCons.Errors.BuildError, e: - exit_status = e.exitstatus - except: - # An exception here is likely a builtin Python exception Python - # code in an SConscript file. Show them precisely what the - # problem was and where it happened. - SCons.Script._SConscript.SConscript_exception() - sys.exit(2) - - memory_stats.print_stats() - count_stats.print_stats() - - if print_objects: - SCons.Debug.listLoggedInstances('*') - #SCons.Debug.dumpLoggedInstances('*') - - if print_memoizer: - SCons.Memoize.Dump("Memoizer (memory cache) hits and misses:") - - # Dump any development debug info that may have been enabled. - # These are purely for internal debugging during development, so - # there's no need to control them with --debug= options; they're - # controlled by changing the source code. - SCons.Debug.dump_caller_counts() - SCons.Taskmaster.dump_stats() - - if print_time: - total_time = time.time() - SCons.Script.start_time - if num_jobs == 1: - ct = cumulative_command_time - else: - if last_command_end is None or first_command_start is None: - ct = 0.0 - else: - ct = last_command_end - first_command_start - scons_time = total_time - sconscript_time - ct - print "Total build time: %f seconds"%total_time - print "Total SConscript file execution time: %f seconds"%sconscript_time - print "Total SCons execution time: %f seconds"%scons_time - print "Total command execution time: %f seconds"%ct - - sys.exit(exit_status) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Script/SConsOptions.py b/scons/scons-local-2.2.0/SCons/Script/SConsOptions.py deleted file mode 100644 index 1d574f8cb..000000000 --- a/scons/scons-local-2.2.0/SCons/Script/SConsOptions.py +++ /dev/null @@ -1,939 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Script/SConsOptions.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import optparse -import re -import sys -import textwrap - -no_hyphen_re = re.compile(r'(\s+|(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))') - -try: - from gettext import gettext -except ImportError: - def gettext(message): - return message -_ = gettext - -import SCons.Node.FS -import SCons.Warnings - -OptionValueError = optparse.OptionValueError -SUPPRESS_HELP = optparse.SUPPRESS_HELP - -diskcheck_all = SCons.Node.FS.diskcheck_types() - -def diskcheck_convert(value): - if value is None: - return [] - if not SCons.Util.is_List(value): - value = value.split(',') - result = [] - for v in value: - v = v.lower() - if v == 'all': - result = diskcheck_all - elif v == 'none': - result = [] - elif v in diskcheck_all: - result.append(v) - else: - raise ValueError(v) - return result - -class SConsValues(optparse.Values): - """ - Holder class for uniform access to SCons options, regardless - of whether or not they can be set on the command line or in the - SConscript files (using the SetOption() function). - - A SCons option value can originate three different ways: - - 1) set on the command line; - 2) set in an SConscript file; - 3) the default setting (from the the op.add_option() - calls in the Parser() function, below). - - The command line always overrides a value set in a SConscript file, - which in turn always overrides default settings. Because we want - to support user-specified options in the SConscript file itself, - though, we may not know about all of the options when the command - line is first parsed, so we can't make all the necessary precedence - decisions at the time the option is configured. - - The solution implemented in this class is to keep these different sets - of settings separate (command line, SConscript file, and default) - and to override the __getattr__() method to check them in turn. - This should allow the rest of the code to just fetch values as - attributes of an instance of this class, without having to worry - about where they came from. - - Note that not all command line options are settable from SConscript - files, and the ones that are must be explicitly added to the - "settable" list in this class, and optionally validated and coerced - in the set_option() method. - """ - - def __init__(self, defaults): - self.__dict__['__defaults__'] = defaults - self.__dict__['__SConscript_settings__'] = {} - - def __getattr__(self, attr): - """ - Fetches an options value, checking first for explicit settings - from the command line (which are direct attributes), then the - SConscript file settings, then the default values. - """ - try: - return self.__dict__[attr] - except KeyError: - try: - return self.__dict__['__SConscript_settings__'][attr] - except KeyError: - return getattr(self.__dict__['__defaults__'], attr) - - settable = [ - 'clean', - 'diskcheck', - 'duplicate', - 'help', - 'implicit_cache', - 'max_drift', - 'md5_chunksize', - 'no_exec', - 'num_jobs', - 'random', - 'stack_size', - 'warn', - ] - - def set_option(self, name, value): - """ - Sets an option from an SConscript file. - """ - if not name in self.settable: - raise SCons.Errors.UserError("This option is not settable from a SConscript file: %s"%name) - - if name == 'num_jobs': - try: - value = int(value) - if value < 1: - raise ValueError - except ValueError: - raise SCons.Errors.UserError("A positive integer is required: %s"%repr(value)) - elif name == 'max_drift': - try: - value = int(value) - except ValueError: - raise SCons.Errors.UserError("An integer is required: %s"%repr(value)) - elif name == 'duplicate': - try: - value = str(value) - except ValueError: - raise SCons.Errors.UserError("A string is required: %s"%repr(value)) - if not value in SCons.Node.FS.Valid_Duplicates: - raise SCons.Errors.UserError("Not a valid duplication style: %s" % value) - # Set the duplicate style right away so it can affect linking - # of SConscript files. - SCons.Node.FS.set_duplicate(value) - elif name == 'diskcheck': - try: - value = diskcheck_convert(value) - except ValueError, v: - raise SCons.Errors.UserError("Not a valid diskcheck value: %s"%v) - if 'diskcheck' not in self.__dict__: - # No --diskcheck= option was specified on the command line. - # Set this right away so it can affect the rest of the - # file/Node lookups while processing the SConscript files. - SCons.Node.FS.set_diskcheck(value) - elif name == 'stack_size': - try: - value = int(value) - except ValueError: - raise SCons.Errors.UserError("An integer is required: %s"%repr(value)) - elif name == 'md5_chunksize': - try: - value = int(value) - except ValueError: - raise SCons.Errors.UserError("An integer is required: %s"%repr(value)) - elif name == 'warn': - if SCons.Util.is_String(value): - value = [value] - value = self.__SConscript_settings__.get(name, []) + value - SCons.Warnings.process_warn_strings(value) - - self.__SConscript_settings__[name] = value - -class SConsOption(optparse.Option): - def convert_value(self, opt, value): - if value is not None: - if self.nargs in (1, '?'): - return self.check_value(opt, value) - else: - return tuple([self.check_value(opt, v) for v in value]) - - def process(self, opt, value, values, parser): - - # First, convert the value(s) to the right type. Howl if any - # value(s) are bogus. - value = self.convert_value(opt, value) - - # And then take whatever action is expected of us. - # This is a separate method to make life easier for - # subclasses to add new actions. - return self.take_action( - self.action, self.dest, opt, value, values, parser) - - def _check_nargs_optional(self): - if self.nargs == '?' and self._short_opts: - fmt = "option %s: nargs='?' is incompatible with short options" - raise SCons.Errors.UserError(fmt % self._short_opts[0]) - - try: - _orig_CONST_ACTIONS = optparse.Option.CONST_ACTIONS - - _orig_CHECK_METHODS = optparse.Option.CHECK_METHODS - - except AttributeError: - # optparse.Option had no CONST_ACTIONS before Python 2.5. - - _orig_CONST_ACTIONS = ("store_const",) - - def _check_const(self): - if self.action not in self.CONST_ACTIONS and self.const is not None: - raise OptionError( - "'const' must not be supplied for action %r" % self.action, - self) - - # optparse.Option collects its list of unbound check functions - # up front. This sucks because it means we can't just override - # the _check_const() function like a normal method, we have to - # actually replace it in the list. This seems to be the most - # straightforward way to do that. - - _orig_CHECK_METHODS = [optparse.Option._check_action, - optparse.Option._check_type, - optparse.Option._check_choice, - optparse.Option._check_dest, - _check_const, - optparse.Option._check_nargs, - optparse.Option._check_callback] - - CHECK_METHODS = _orig_CHECK_METHODS + [_check_nargs_optional] - - CONST_ACTIONS = _orig_CONST_ACTIONS + optparse.Option.TYPED_ACTIONS - -class SConsOptionGroup(optparse.OptionGroup): - """ - A subclass for SCons-specific option groups. - - The only difference between this and the base class is that we print - the group's help text flush left, underneath their own title but - lined up with the normal "SCons Options". - """ - def format_help(self, formatter): - """ - Format an option group's help text, outdenting the title so it's - flush with the "SCons Options" title we print at the top. - """ - formatter.dedent() - result = formatter.format_heading(self.title) - formatter.indent() - result = result + optparse.OptionContainer.format_help(self, formatter) - return result - -class SConsOptionParser(optparse.OptionParser): - preserve_unknown_options = False - - def error(self, msg): - self.print_usage(sys.stderr) - sys.stderr.write("SCons error: %s\n" % msg) - sys.exit(2) - - def _process_long_opt(self, rargs, values): - """ - SCons-specific processing of long options. - - This is copied directly from the normal - optparse._process_long_opt() method, except that, if configured - to do so, we catch the exception thrown when an unknown option - is encountered and just stick it back on the "leftover" arguments - for later (re-)processing. - """ - arg = rargs.pop(0) - - # Value explicitly attached to arg? Pretend it's the next - # argument. - if "=" in arg: - (opt, next_arg) = arg.split("=", 1) - rargs.insert(0, next_arg) - had_explicit_value = True - else: - opt = arg - had_explicit_value = False - - try: - opt = self._match_long_opt(opt) - except optparse.BadOptionError: - if self.preserve_unknown_options: - # SCons-specific: if requested, add unknown options to - # the "leftover arguments" list for later processing. - self.largs.append(arg) - if had_explicit_value: - # The unknown option will be re-processed later, - # so undo the insertion of the explicit value. - rargs.pop(0) - return - raise - - option = self._long_opt[opt] - if option.takes_value(): - nargs = option.nargs - if nargs == '?': - if had_explicit_value: - value = rargs.pop(0) - else: - value = option.const - elif len(rargs) < nargs: - if nargs == 1: - self.error(_("%s option requires an argument") % opt) - else: - self.error(_("%s option requires %d arguments") - % (opt, nargs)) - elif nargs == 1: - value = rargs.pop(0) - else: - value = tuple(rargs[0:nargs]) - del rargs[0:nargs] - - elif had_explicit_value: - self.error(_("%s option does not take a value") % opt) - - else: - value = None - - option.process(opt, value, values, self) - - def add_local_option(self, *args, **kw): - """ - Adds a local option to the parser. - - This is initiated by a SetOption() call to add a user-defined - command-line option. We add the option to a separate option - group for the local options, creating the group if necessary. - """ - try: - group = self.local_option_group - except AttributeError: - group = SConsOptionGroup(self, 'Local Options') - group = self.add_option_group(group) - self.local_option_group = group - - result = group.add_option(*args, **kw) - - if result: - # The option was added succesfully. We now have to add the - # default value to our object that holds the default values - # (so that an attempt to fetch the option's attribute will - # yield the default value when not overridden) and then - # we re-parse the leftover command-line options, so that - # any value overridden on the command line is immediately - # available if the user turns around and does a GetOption() - # right away. - setattr(self.values.__defaults__, result.dest, result.default) - self.parse_args(self.largs, self.values) - - return result - -class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter): - def format_usage(self, usage): - return "usage: %s\n" % usage - - def format_heading(self, heading): - """ - This translates any heading of "options" or "Options" into - "SCons Options." Unfortunately, we have to do this here, - because those titles are hard-coded in the optparse calls. - """ - if heading == 'options': - # The versions of optparse.py shipped with Pythons 2.3 and - # 2.4 pass this in uncapitalized; override that so we get - # consistent output on all versions. - heading = "Options" - if heading == 'Options': - heading = "SCons Options" - return optparse.IndentedHelpFormatter.format_heading(self, heading) - - def format_option(self, option): - """ - A copy of the normal optparse.IndentedHelpFormatter.format_option() - method. This has been snarfed so we can modify text wrapping to - out liking: - - -- add our own regular expression that doesn't break on hyphens - (so things like --no-print-directory don't get broken); - - -- wrap the list of options themselves when it's too long - (the wrapper.fill(opts) call below); - - -- set the subsequent_indent when wrapping the help_text. - """ - # The help for each option consists of two parts: - # * the opt strings and metavars - # eg. ("-x", or "-fFILENAME, --file=FILENAME") - # * the user-supplied help string - # eg. ("turn on expert mode", "read data from FILENAME") - # - # If possible, we write both of these on the same line: - # -x turn on expert mode - # - # But if the opt string list is too long, we put the help - # string on a second line, indented to the same column it would - # start in if it fit on the first line. - # -fFILENAME, --file=FILENAME - # read data from FILENAME - result = [] - - try: - opts = self.option_strings[option] - except AttributeError: - # The Python 2.3 version of optparse attaches this to - # to the option argument, not to this object. - opts = option.option_strings - - opt_width = self.help_position - self.current_indent - 2 - if len(opts) > opt_width: - wrapper = textwrap.TextWrapper(width=self.width, - initial_indent = ' ', - subsequent_indent = ' ') - wrapper.wordsep_re = no_hyphen_re - opts = wrapper.fill(opts) + '\n' - indent_first = self.help_position - else: # start help on same line as opts - opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts) - indent_first = 0 - result.append(opts) - if option.help: - - try: - expand_default = self.expand_default - except AttributeError: - # The HelpFormatter base class in the Python 2.3 version - # of optparse has no expand_default() method. - help_text = option.help - else: - help_text = expand_default(option) - - # SCons: indent every line of the help text but the first. - wrapper = textwrap.TextWrapper(width=self.help_width, - subsequent_indent = ' ') - wrapper.wordsep_re = no_hyphen_re - help_lines = wrapper.wrap(help_text) - result.append("%*s%s\n" % (indent_first, "", help_lines[0])) - for line in help_lines[1:]: - result.append("%*s%s\n" % (self.help_position, "", line)) - elif opts[-1] != "\n": - result.append("\n") - return "".join(result) - - # For consistent help output across Python versions, we provide a - # subclass copy of format_option_strings() and these two variables. - # This is necessary (?) for Python2.3, which otherwise concatenates - # a short option with its metavar. - _short_opt_fmt = "%s %s" - _long_opt_fmt = "%s=%s" - - def format_option_strings(self, option): - """Return a comma-separated list of option strings & metavariables.""" - if option.takes_value(): - metavar = option.metavar or option.dest.upper() - short_opts = [] - for sopt in option._short_opts: - short_opts.append(self._short_opt_fmt % (sopt, metavar)) - long_opts = [] - for lopt in option._long_opts: - long_opts.append(self._long_opt_fmt % (lopt, metavar)) - else: - short_opts = option._short_opts - long_opts = option._long_opts - - if self.short_first: - opts = short_opts + long_opts - else: - opts = long_opts + short_opts - - return ", ".join(opts) - -def Parser(version): - """ - Returns an options parser object initialized with the standard - SCons options. - """ - - formatter = SConsIndentedHelpFormatter(max_help_position=30) - - op = SConsOptionParser(option_class=SConsOption, - add_help_option=False, - formatter=formatter, - usage="usage: scons [OPTION] [TARGET] ...",) - - op.preserve_unknown_options = True - op.version = version - - # Add the options to the parser we just created. - # - # These are in the order we want them to show up in the -H help - # text, basically alphabetical. Each op.add_option() call below - # should have a consistent format: - # - # op.add_option("-L", "--long-option-name", - # nargs=1, type="string", - # dest="long_option_name", default='foo', - # action="callback", callback=opt_long_option, - # help="help text goes here", - # metavar="VAR") - # - # Even though the optparse module constructs reasonable default - # destination names from the long option names, we're going to be - # explicit about each one for easier readability and so this code - # will at least show up when grepping the source for option attribute - # names, or otherwise browsing the source code. - - # options ignored for compatibility - def opt_ignore(option, opt, value, parser): - sys.stderr.write("Warning: ignoring %s option\n" % opt) - op.add_option("-b", "-d", "-e", "-m", "-S", "-t", "-w", - "--environment-overrides", - "--no-keep-going", - "--no-print-directory", - "--print-directory", - "--stop", - "--touch", - action="callback", callback=opt_ignore, - help="Ignored for compatibility.") - - op.add_option('-c', '--clean', '--remove', - dest="clean", default=False, - action="store_true", - help="Remove specified targets and dependencies.") - - op.add_option('-C', '--directory', - nargs=1, type="string", - dest="directory", default=[], - action="append", - help="Change to DIR before doing anything.", - metavar="DIR") - - op.add_option('--cache-debug', - nargs=1, - dest="cache_debug", default=None, - action="store", - help="Print CacheDir debug info to FILE.", - metavar="FILE") - - op.add_option('--cache-disable', '--no-cache', - dest='cache_disable', default=False, - action="store_true", - help="Do not retrieve built targets from CacheDir.") - - op.add_option('--cache-force', '--cache-populate', - dest='cache_force', default=False, - action="store_true", - help="Copy already-built targets into the CacheDir.") - - op.add_option('--cache-show', - dest='cache_show', default=False, - action="store_true", - help="Print build actions for files from CacheDir.") - - config_options = ["auto", "force" ,"cache"] - - def opt_config(option, opt, value, parser, c_options=config_options): - if not value in c_options: - raise OptionValueError("Warning: %s is not a valid config type" % value) - setattr(parser.values, option.dest, value) - opt_config_help = "Controls Configure subsystem: %s." \ - % ", ".join(config_options) - op.add_option('--config', - nargs=1, type="string", - dest="config", default="auto", - action="callback", callback=opt_config, - help = opt_config_help, - metavar="MODE") - - op.add_option('-D', - dest="climb_up", default=None, - action="store_const", const=2, - help="Search up directory tree for SConstruct, " - "build all Default() targets.") - - deprecated_debug_options = { - "dtree" : '; please use --tree=derived instead', - "nomemoizer" : ' and has no effect', - "stree" : '; please use --tree=all,status instead', - "tree" : '; please use --tree=all instead', - } - - debug_options = ["count", "duplicate", "explain", "findlibs", - "includes", "memoizer", "memory", "objects", - "pdb", "prepare", "presub", "stacktrace", - "time"] + list(deprecated_debug_options.keys()) - - def opt_debug(option, opt, value, parser, - debug_options=debug_options, - deprecated_debug_options=deprecated_debug_options): - if value in debug_options: - parser.values.debug.append(value) - if value in deprecated_debug_options.keys(): - try: - parser.values.delayed_warnings - except AttributeError: - parser.values.delayed_warnings = [] - msg = deprecated_debug_options[value] - w = "The --debug=%s option is deprecated%s." % (value, msg) - t = (SCons.Warnings.DeprecatedDebugOptionsWarning, w) - parser.values.delayed_warnings.append(t) - else: - raise OptionValueError("Warning: %s is not a valid debug type" % value) - opt_debug_help = "Print various types of debugging information: %s." \ - % ", ".join(debug_options) - op.add_option('--debug', - nargs=1, type="string", - dest="debug", default=[], - action="callback", callback=opt_debug, - help=opt_debug_help, - metavar="TYPE") - - def opt_diskcheck(option, opt, value, parser): - try: - diskcheck_value = diskcheck_convert(value) - except ValueError, e: - raise OptionValueError("Warning: `%s' is not a valid diskcheck type" % e) - setattr(parser.values, option.dest, diskcheck_value) - - op.add_option('--diskcheck', - nargs=1, type="string", - dest='diskcheck', default=None, - action="callback", callback=opt_diskcheck, - help="Enable specific on-disk checks.", - metavar="TYPE") - - def opt_duplicate(option, opt, value, parser): - if not value in SCons.Node.FS.Valid_Duplicates: - raise OptionValueError("`%s' is not a valid duplication style." % value) - setattr(parser.values, option.dest, value) - # Set the duplicate style right away so it can affect linking - # of SConscript files. - SCons.Node.FS.set_duplicate(value) - - opt_duplicate_help = "Set the preferred duplication methods. Must be one of " \ - + ", ".join(SCons.Node.FS.Valid_Duplicates) - - op.add_option('--duplicate', - nargs=1, type="string", - dest="duplicate", default='hard-soft-copy', - action="callback", callback=opt_duplicate, - help=opt_duplicate_help) - - op.add_option('-f', '--file', '--makefile', '--sconstruct', - nargs=1, type="string", - dest="file", default=[], - action="append", - help="Read FILE as the top-level SConstruct file.") - - op.add_option('-h', '--help', - dest="help", default=False, - action="store_true", - help="Print defined help message, or this one.") - - op.add_option("-H", "--help-options", - action="help", - help="Print this message and exit.") - - op.add_option('-i', '--ignore-errors', - dest='ignore_errors', default=False, - action="store_true", - help="Ignore errors from build actions.") - - op.add_option('-I', '--include-dir', - nargs=1, - dest='include_dir', default=[], - action="append", - help="Search DIR for imported Python modules.", - metavar="DIR") - - op.add_option('--implicit-cache', - dest='implicit_cache', default=False, - action="store_true", - help="Cache implicit dependencies") - - def opt_implicit_deps(option, opt, value, parser): - setattr(parser.values, 'implicit_cache', True) - setattr(parser.values, option.dest, True) - - op.add_option('--implicit-deps-changed', - dest="implicit_deps_changed", default=False, - action="callback", callback=opt_implicit_deps, - help="Ignore cached implicit dependencies.") - - op.add_option('--implicit-deps-unchanged', - dest="implicit_deps_unchanged", default=False, - action="callback", callback=opt_implicit_deps, - help="Ignore changes in implicit dependencies.") - - op.add_option('--interact', '--interactive', - dest='interactive', default=False, - action="store_true", - help="Run in interactive mode.") - - op.add_option('-j', '--jobs', - nargs=1, type="int", - dest="num_jobs", default=1, - action="store", - help="Allow N jobs at once.", - metavar="N") - - op.add_option('-k', '--keep-going', - dest='keep_going', default=False, - action="store_true", - help="Keep going when a target can't be made.") - - op.add_option('--max-drift', - nargs=1, type="int", - dest='max_drift', default=SCons.Node.FS.default_max_drift, - action="store", - help="Set maximum system clock drift to N seconds.", - metavar="N") - - op.add_option('--md5-chunksize', - nargs=1, type="int", - dest='md5_chunksize', default=SCons.Node.FS.File.md5_chunksize, - action="store", - help="Set chunk-size for MD5 signature computation to N kilobytes.", - metavar="N") - - op.add_option('-n', '--no-exec', '--just-print', '--dry-run', '--recon', - dest='no_exec', default=False, - action="store_true", - help="Don't build; just print commands.") - - op.add_option('--no-site-dir', - dest='no_site_dir', default=False, - action="store_true", - help="Don't search or use the usual site_scons dir.") - - op.add_option('--profile', - nargs=1, - dest="profile_file", default=None, - action="store", - help="Profile SCons and put results in FILE.", - metavar="FILE") - - op.add_option('-q', '--question', - dest="question", default=False, - action="store_true", - help="Don't build; exit status says if up to date.") - - op.add_option('-Q', - dest='no_progress', default=False, - action="store_true", - help="Suppress \"Reading/Building\" progress messages.") - - op.add_option('--random', - dest="random", default=False, - action="store_true", - help="Build dependencies in random order.") - - op.add_option('-s', '--silent', '--quiet', - dest="silent", default=False, - action="store_true", - help="Don't print commands.") - - op.add_option('--site-dir', - nargs=1, - dest='site_dir', default=None, - action="store", - help="Use DIR instead of the usual site_scons dir.", - metavar="DIR") - - op.add_option('--stack-size', - nargs=1, type="int", - dest='stack_size', - action="store", - help="Set the stack size of the threads used to run jobs to N kilobytes.", - metavar="N") - - op.add_option('--taskmastertrace', - nargs=1, - dest="taskmastertrace_file", default=None, - action="store", - help="Trace Node evaluation to FILE.", - metavar="FILE") - - tree_options = ["all", "derived", "prune", "status"] - - def opt_tree(option, opt, value, parser, tree_options=tree_options): - import Main - tp = Main.TreePrinter() - for o in value.split(','): - if o == 'all': - tp.derived = False - elif o == 'derived': - tp.derived = True - elif o == 'prune': - tp.prune = True - elif o == 'status': - tp.status = True - else: - raise OptionValueError("Warning: %s is not a valid --tree option" % o) - parser.values.tree_printers.append(tp) - - opt_tree_help = "Print a dependency tree in various formats: %s." \ - % ", ".join(tree_options) - - op.add_option('--tree', - nargs=1, type="string", - dest="tree_printers", default=[], - action="callback", callback=opt_tree, - help=opt_tree_help, - metavar="OPTIONS") - - op.add_option('-u', '--up', '--search-up', - dest="climb_up", default=0, - action="store_const", const=1, - help="Search up directory tree for SConstruct, " - "build targets at or below current directory.") - - op.add_option('-U', - dest="climb_up", default=0, - action="store_const", const=3, - help="Search up directory tree for SConstruct, " - "build Default() targets from local SConscript.") - - def opt_version(option, opt, value, parser): - sys.stdout.write(parser.version + '\n') - sys.exit(0) - op.add_option("-v", "--version", - action="callback", callback=opt_version, - help="Print the SCons version number and exit.") - - def opt_warn(option, opt, value, parser, tree_options=tree_options): - if SCons.Util.is_String(value): - value = value.split(',') - parser.values.warn.extend(value) - - op.add_option('--warn', '--warning', - nargs=1, type="string", - dest="warn", default=[], - action="callback", callback=opt_warn, - help="Enable or disable warnings.", - metavar="WARNING-SPEC") - - op.add_option('-Y', '--repository', '--srcdir', - nargs=1, - dest="repository", default=[], - action="append", - help="Search REPOSITORY for source and target files.") - - # Options from Make and Cons classic that we do not yet support, - # but which we may support someday and whose (potential) meanings - # we don't want to change. These all get a "the -X option is not - # yet implemented" message and don't show up in the help output. - - def opt_not_yet(option, opt, value, parser): - msg = "Warning: the %s option is not yet implemented\n" % opt - sys.stderr.write(msg) - - op.add_option('-l', '--load-average', '--max-load', - nargs=1, type="float", - dest="load_average", default=0, - action="callback", callback=opt_not_yet, - # action="store", - # help="Don't start multiple jobs unless load is below " - # "LOAD-AVERAGE." - help=SUPPRESS_HELP) - op.add_option('--list-actions', - dest="list_actions", - action="callback", callback=opt_not_yet, - # help="Don't build; list files and build actions." - help=SUPPRESS_HELP) - op.add_option('--list-derived', - dest="list_derived", - action="callback", callback=opt_not_yet, - # help="Don't build; list files that would be built." - help=SUPPRESS_HELP) - op.add_option('--list-where', - dest="list_where", - action="callback", callback=opt_not_yet, - # help="Don't build; list files and where defined." - help=SUPPRESS_HELP) - op.add_option('-o', '--old-file', '--assume-old', - nargs=1, type="string", - dest="old_file", default=[], - action="callback", callback=opt_not_yet, - # action="append", - # help = "Consider FILE to be old; don't rebuild it." - help=SUPPRESS_HELP) - op.add_option('--override', - nargs=1, type="string", - action="callback", callback=opt_not_yet, - dest="override", - # help="Override variables as specified in FILE." - help=SUPPRESS_HELP) - op.add_option('-p', - action="callback", callback=opt_not_yet, - dest="p", - # help="Print internal environments/objects." - help=SUPPRESS_HELP) - op.add_option('-r', '-R', '--no-builtin-rules', '--no-builtin-variables', - action="callback", callback=opt_not_yet, - dest="no_builtin_rules", - # help="Clear default environments and variables." - help=SUPPRESS_HELP) - op.add_option('--write-filenames', - nargs=1, type="string", - dest="write_filenames", - action="callback", callback=opt_not_yet, - # help="Write all filenames examined into FILE." - help=SUPPRESS_HELP) - op.add_option('-W', '--new-file', '--assume-new', '--what-if', - nargs=1, type="string", - dest="new_file", - action="callback", callback=opt_not_yet, - # help="Consider FILE to be changed." - help=SUPPRESS_HELP) - op.add_option('--warn-undefined-variables', - dest="warn_undefined_variables", - action="callback", callback=opt_not_yet, - # help="Warn when an undefined variable is referenced." - help=SUPPRESS_HELP) - - return op - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Script/SConscript.py b/scons/scons-local-2.2.0/SCons/Script/SConscript.py deleted file mode 100644 index 18a9e310a..000000000 --- a/scons/scons-local-2.2.0/SCons/Script/SConscript.py +++ /dev/null @@ -1,640 +0,0 @@ -"""SCons.Script.SConscript - -This module defines the Python API provided to SConscript and SConstruct -files. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -from __future__ import division - -__revision__ = "src/engine/SCons/Script/SConscript.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons -import SCons.Action -import SCons.Builder -import SCons.Defaults -import SCons.Environment -import SCons.Errors -import SCons.Node -import SCons.Node.Alias -import SCons.Node.FS -import SCons.Platform -import SCons.SConf -import SCons.Script.Main -import SCons.Tool -import SCons.Util - -import collections -import os -import os.path -import re -import sys -import traceback - -# The following variables used to live in this module. Some -# SConscript files out there may have referred to them directly as -# SCons.Script.SConscript.*. This is now supported by some special -# handling towards the bottom of the SConscript.__init__.py module. -#Arguments = {} -#ArgList = [] -#BuildTargets = TargetList() -#CommandLineTargets = [] -#DefaultTargets = [] - -class SConscriptReturn(Exception): - pass - -launch_dir = os.path.abspath(os.curdir) - -GlobalDict = None - -# global exports set by Export(): -global_exports = {} - -# chdir flag -sconscript_chdir = 1 - -def get_calling_namespaces(): - """Return the locals and globals for the function that called - into this module in the current call stack.""" - try: 1//0 - except ZeroDivisionError: - # Don't start iterating with the current stack-frame to - # prevent creating reference cycles (f_back is safe). - frame = sys.exc_info()[2].tb_frame.f_back - - # Find the first frame that *isn't* from this file. This means - # that we expect all of the SCons frames that implement an Export() - # or SConscript() call to be in this file, so that we can identify - # the first non-Script.SConscript frame as the user's local calling - # environment, and the locals and globals dictionaries from that - # frame as the calling namespaces. See the comment below preceding - # the DefaultEnvironmentCall block for even more explanation. - while frame.f_globals.get("__name__") == __name__: - frame = frame.f_back - - return frame.f_locals, frame.f_globals - - -def compute_exports(exports): - """Compute a dictionary of exports given one of the parameters - to the Export() function or the exports argument to SConscript().""" - - loc, glob = get_calling_namespaces() - - retval = {} - try: - for export in exports: - if SCons.Util.is_Dict(export): - retval.update(export) - else: - try: - retval[export] = loc[export] - except KeyError: - retval[export] = glob[export] - except KeyError, x: - raise SCons.Errors.UserError("Export of non-existent variable '%s'"%x) - - return retval - -class Frame(object): - """A frame on the SConstruct/SConscript call stack""" - def __init__(self, fs, exports, sconscript): - self.globals = BuildDefaultGlobals() - self.retval = None - self.prev_dir = fs.getcwd() - self.exports = compute_exports(exports) # exports from the calling SConscript - # make sure the sconscript attr is a Node. - if isinstance(sconscript, SCons.Node.Node): - self.sconscript = sconscript - elif sconscript == '-': - self.sconscript = None - else: - self.sconscript = fs.File(str(sconscript)) - -# the SConstruct/SConscript call stack: -call_stack = [] - -# For documentation on the methods in this file, see the scons man-page - -def Return(*vars, **kw): - retval = [] - try: - fvars = SCons.Util.flatten(vars) - for var in fvars: - for v in var.split(): - retval.append(call_stack[-1].globals[v]) - except KeyError, x: - raise SCons.Errors.UserError("Return of non-existent variable '%s'"%x) - - if len(retval) == 1: - call_stack[-1].retval = retval[0] - else: - call_stack[-1].retval = tuple(retval) - - stop = kw.get('stop', True) - - if stop: - raise SConscriptReturn - - -stack_bottom = '% Stack boTTom %' # hard to define a variable w/this name :) - -def _SConscript(fs, *files, **kw): - top = fs.Top - sd = fs.SConstruct_dir.rdir() - exports = kw.get('exports', []) - - # evaluate each SConscript file - results = [] - for fn in files: - call_stack.append(Frame(fs, exports, fn)) - old_sys_path = sys.path - try: - SCons.Script.sconscript_reading = SCons.Script.sconscript_reading + 1 - if fn == "-": - exec sys.stdin in call_stack[-1].globals - else: - if isinstance(fn, SCons.Node.Node): - f = fn - else: - f = fs.File(str(fn)) - _file_ = None - - # Change directory to the top of the source - # tree to make sure the os's cwd and the cwd of - # fs match so we can open the SConscript. - fs.chdir(top, change_os_dir=1) - if f.rexists(): - actual = f.rfile() - _file_ = open(actual.get_abspath(), "r") - elif f.srcnode().rexists(): - actual = f.srcnode().rfile() - _file_ = open(actual.get_abspath(), "r") - elif f.has_src_builder(): - # The SConscript file apparently exists in a source - # code management system. Build it, but then clear - # the builder so that it doesn't get built *again* - # during the actual build phase. - f.build() - f.built() - f.builder_set(None) - if f.exists(): - _file_ = open(f.get_abspath(), "r") - if _file_: - # Chdir to the SConscript directory. Use a path - # name relative to the SConstruct file so that if - # we're using the -f option, we're essentially - # creating a parallel SConscript directory structure - # in our local directory tree. - # - # XXX This is broken for multiple-repository cases - # where the SConstruct and SConscript files might be - # in different Repositories. For now, cross that - # bridge when someone comes to it. - try: - src_dir = kw['src_dir'] - except KeyError: - ldir = fs.Dir(f.dir.get_path(sd)) - else: - ldir = fs.Dir(src_dir) - if not ldir.is_under(f.dir): - # They specified a source directory, but - # it's above the SConscript directory. - # Do the sensible thing and just use the - # SConcript directory. - ldir = fs.Dir(f.dir.get_path(sd)) - try: - fs.chdir(ldir, change_os_dir=sconscript_chdir) - except OSError: - # There was no local directory, so we should be - # able to chdir to the Repository directory. - # Note that we do this directly, not through - # fs.chdir(), because we still need to - # interpret the stuff within the SConscript file - # relative to where we are logically. - fs.chdir(ldir, change_os_dir=0) - os.chdir(actual.dir.get_abspath()) - - # Append the SConscript directory to the beginning - # of sys.path so Python modules in the SConscript - # directory can be easily imported. - sys.path = [ f.dir.get_abspath() ] + sys.path - - # This is the magic line that actually reads up - # and executes the stuff in the SConscript file. - # The locals for this frame contain the special - # bottom-of-the-stack marker so that any - # exceptions that occur when processing this - # SConscript can base the printed frames at this - # level and not show SCons internals as well. - call_stack[-1].globals.update({stack_bottom:1}) - old_file = call_stack[-1].globals.get('__file__') - try: - del call_stack[-1].globals['__file__'] - except KeyError: - pass - try: - try: - exec _file_ in call_stack[-1].globals - except SConscriptReturn: - pass - finally: - if old_file is not None: - call_stack[-1].globals.update({__file__:old_file}) - else: - SCons.Warnings.warn(SCons.Warnings.MissingSConscriptWarning, - "Ignoring missing SConscript '%s'" % f.path) - - finally: - SCons.Script.sconscript_reading = SCons.Script.sconscript_reading - 1 - sys.path = old_sys_path - frame = call_stack.pop() - try: - fs.chdir(frame.prev_dir, change_os_dir=sconscript_chdir) - except OSError: - # There was no local directory, so chdir to the - # Repository directory. Like above, we do this - # directly. - fs.chdir(frame.prev_dir, change_os_dir=0) - rdir = frame.prev_dir.rdir() - rdir._create() # Make sure there's a directory there. - try: - os.chdir(rdir.get_abspath()) - except OSError, e: - # We still couldn't chdir there, so raise the error, - # but only if actions are being executed. - # - # If the -n option was used, the directory would *not* - # have been created and we should just carry on and - # let things muddle through. This isn't guaranteed - # to work if the SConscript files are reading things - # from disk (for example), but it should work well - # enough for most configurations. - if SCons.Action.execute_actions: - raise e - - results.append(frame.retval) - - # if we only have one script, don't return a tuple - if len(results) == 1: - return results[0] - else: - return tuple(results) - -def SConscript_exception(file=sys.stderr): - """Print an exception stack trace just for the SConscript file(s). - This will show users who have Python errors where the problem is, - without cluttering the output with all of the internal calls leading - up to where we exec the SConscript.""" - exc_type, exc_value, exc_tb = sys.exc_info() - tb = exc_tb - while tb and stack_bottom not in tb.tb_frame.f_locals: - tb = tb.tb_next - if not tb: - # We did not find our exec statement, so this was actually a bug - # in SCons itself. Show the whole stack. - tb = exc_tb - stack = traceback.extract_tb(tb) - try: - type = exc_type.__name__ - except AttributeError: - type = str(exc_type) - if type[:11] == "exceptions.": - type = type[11:] - file.write('%s: %s:\n' % (type, exc_value)) - for fname, line, func, text in stack: - file.write(' File "%s", line %d:\n' % (fname, line)) - file.write(' %s\n' % text) - -def annotate(node): - """Annotate a node with the stack frame describing the - SConscript file and line number that created it.""" - tb = sys.exc_info()[2] - while tb and stack_bottom not in tb.tb_frame.f_locals: - tb = tb.tb_next - if not tb: - # We did not find any exec of an SConscript file: what?! - raise SCons.Errors.InternalError("could not find SConscript stack frame") - node.creator = traceback.extract_stack(tb)[0] - -# The following line would cause each Node to be annotated using the -# above function. Unfortunately, this is a *huge* performance hit, so -# leave this disabled until we find a more efficient mechanism. -#SCons.Node.Annotate = annotate - -class SConsEnvironment(SCons.Environment.Base): - """An Environment subclass that contains all of the methods that - are particular to the wrapper SCons interface and which aren't - (or shouldn't be) part of the build engine itself. - - Note that not all of the methods of this class have corresponding - global functions, there are some private methods. - """ - - # - # Private methods of an SConsEnvironment. - # - def _exceeds_version(self, major, minor, v_major, v_minor): - """Return 1 if 'major' and 'minor' are greater than the version - in 'v_major' and 'v_minor', and 0 otherwise.""" - return (major > v_major or (major == v_major and minor > v_minor)) - - def _get_major_minor_revision(self, version_string): - """Split a version string into major, minor and (optionally) - revision parts. - - This is complicated by the fact that a version string can be - something like 3.2b1.""" - version = version_string.split(' ')[0].split('.') - v_major = int(version[0]) - v_minor = int(re.match('\d+', version[1]).group()) - if len(version) >= 3: - v_revision = int(re.match('\d+', version[2]).group()) - else: - v_revision = 0 - return v_major, v_minor, v_revision - - def _get_SConscript_filenames(self, ls, kw): - """ - Convert the parameters passed to SConscript() calls into a list - of files and export variables. If the parameters are invalid, - throws SCons.Errors.UserError. Returns a tuple (l, e) where l - is a list of SConscript filenames and e is a list of exports. - """ - exports = [] - - if len(ls) == 0: - try: - dirs = kw["dirs"] - except KeyError: - raise SCons.Errors.UserError("Invalid SConscript usage - no parameters") - - if not SCons.Util.is_List(dirs): - dirs = [ dirs ] - dirs = list(map(str, dirs)) - - name = kw.get('name', 'SConscript') - - files = [os.path.join(n, name) for n in dirs] - - elif len(ls) == 1: - - files = ls[0] - - elif len(ls) == 2: - - files = ls[0] - exports = self.Split(ls[1]) - - else: - - raise SCons.Errors.UserError("Invalid SConscript() usage - too many arguments") - - if not SCons.Util.is_List(files): - files = [ files ] - - if kw.get('exports'): - exports.extend(self.Split(kw['exports'])) - - variant_dir = kw.get('variant_dir') or kw.get('build_dir') - if variant_dir: - if len(files) != 1: - raise SCons.Errors.UserError("Invalid SConscript() usage - can only specify one SConscript with a variant_dir") - duplicate = kw.get('duplicate', 1) - src_dir = kw.get('src_dir') - if not src_dir: - src_dir, fname = os.path.split(str(files[0])) - files = [os.path.join(str(variant_dir), fname)] - else: - if not isinstance(src_dir, SCons.Node.Node): - src_dir = self.fs.Dir(src_dir) - fn = files[0] - if not isinstance(fn, SCons.Node.Node): - fn = self.fs.File(fn) - if fn.is_under(src_dir): - # Get path relative to the source directory. - fname = fn.get_path(src_dir) - files = [os.path.join(str(variant_dir), fname)] - else: - files = [fn.abspath] - kw['src_dir'] = variant_dir - self.fs.VariantDir(variant_dir, src_dir, duplicate) - - return (files, exports) - - # - # Public methods of an SConsEnvironment. These get - # entry points in the global name space so they can be called - # as global functions. - # - - def Configure(self, *args, **kw): - if not SCons.Script.sconscript_reading: - raise SCons.Errors.UserError("Calling Configure from Builders is not supported.") - kw['_depth'] = kw.get('_depth', 0) + 1 - return SCons.Environment.Base.Configure(self, *args, **kw) - - def Default(self, *targets): - SCons.Script._Set_Default_Targets(self, targets) - - def EnsureSConsVersion(self, major, minor, revision=0): - """Exit abnormally if the SCons version is not late enough.""" - scons_ver = self._get_major_minor_revision(SCons.__version__) - if scons_ver < (major, minor, revision): - if revision: - scons_ver_string = '%d.%d.%d' % (major, minor, revision) - else: - scons_ver_string = '%d.%d' % (major, minor) - print "SCons %s or greater required, but you have SCons %s" % \ - (scons_ver_string, SCons.__version__) - sys.exit(2) - - def EnsurePythonVersion(self, major, minor): - """Exit abnormally if the Python version is not late enough.""" - try: - v_major, v_minor, v_micro, release, serial = sys.version_info - python_ver = (v_major, v_minor) - except AttributeError: - python_ver = self._get_major_minor_revision(sys.version)[:2] - if python_ver < (major, minor): - v = sys.version.split(" ", 1)[0] - print "Python %d.%d or greater required, but you have Python %s" %(major,minor,v) - sys.exit(2) - - def Exit(self, value=0): - sys.exit(value) - - def Export(self, *vars, **kw): - for var in vars: - global_exports.update(compute_exports(self.Split(var))) - global_exports.update(kw) - - def GetLaunchDir(self): - global launch_dir - return launch_dir - - def GetOption(self, name): - name = self.subst(name) - return SCons.Script.Main.GetOption(name) - - def Help(self, text): - text = self.subst(text, raw=1) - SCons.Script.HelpFunction(text) - - def Import(self, *vars): - try: - frame = call_stack[-1] - globals = frame.globals - exports = frame.exports - for var in vars: - var = self.Split(var) - for v in var: - if v == '*': - globals.update(global_exports) - globals.update(exports) - else: - if v in exports: - globals[v] = exports[v] - else: - globals[v] = global_exports[v] - except KeyError,x: - raise SCons.Errors.UserError("Import of non-existent variable '%s'"%x) - - def SConscript(self, *ls, **kw): - if 'build_dir' in kw: - msg = """The build_dir keyword has been deprecated; use the variant_dir keyword instead.""" - SCons.Warnings.warn(SCons.Warnings.DeprecatedBuildDirWarning, msg) - def subst_element(x, subst=self.subst): - if SCons.Util.is_List(x): - x = list(map(subst, x)) - else: - x = subst(x) - return x - ls = list(map(subst_element, ls)) - subst_kw = {} - for key, val in kw.items(): - if SCons.Util.is_String(val): - val = self.subst(val) - elif SCons.Util.is_List(val): - result = [] - for v in val: - if SCons.Util.is_String(v): - v = self.subst(v) - result.append(v) - val = result - subst_kw[key] = val - - files, exports = self._get_SConscript_filenames(ls, subst_kw) - subst_kw['exports'] = exports - return _SConscript(self.fs, *files, **subst_kw) - - def SConscriptChdir(self, flag): - global sconscript_chdir - sconscript_chdir = flag - - def SetOption(self, name, value): - name = self.subst(name) - SCons.Script.Main.SetOption(name, value) - -# -# -# -SCons.Environment.Environment = SConsEnvironment - -def Configure(*args, **kw): - if not SCons.Script.sconscript_reading: - raise SCons.Errors.UserError("Calling Configure from Builders is not supported.") - kw['_depth'] = 1 - return SCons.SConf.SConf(*args, **kw) - -# It's very important that the DefaultEnvironmentCall() class stay in this -# file, with the get_calling_namespaces() function, the compute_exports() -# function, the Frame class and the SConsEnvironment.Export() method. -# These things make up the calling stack leading up to the actual global -# Export() or SConscript() call that the user issued. We want to allow -# users to export local variables that they define, like so: -# -# def func(): -# x = 1 -# Export('x') -# -# To support this, the get_calling_namespaces() function assumes that -# the *first* stack frame that's not from this file is the local frame -# for the Export() or SConscript() call. - -_DefaultEnvironmentProxy = None - -def get_DefaultEnvironmentProxy(): - global _DefaultEnvironmentProxy - if not _DefaultEnvironmentProxy: - default_env = SCons.Defaults.DefaultEnvironment() - _DefaultEnvironmentProxy = SCons.Environment.NoSubstitutionProxy(default_env) - return _DefaultEnvironmentProxy - -class DefaultEnvironmentCall(object): - """A class that implements "global function" calls of - Environment methods by fetching the specified method from the - DefaultEnvironment's class. Note that this uses an intermediate - proxy class instead of calling the DefaultEnvironment method - directly so that the proxy can override the subst() method and - thereby prevent expansion of construction variables (since from - the user's point of view this was called as a global function, - with no associated construction environment).""" - def __init__(self, method_name, subst=0): - self.method_name = method_name - if subst: - self.factory = SCons.Defaults.DefaultEnvironment - else: - self.factory = get_DefaultEnvironmentProxy - def __call__(self, *args, **kw): - env = self.factory() - method = getattr(env, self.method_name) - return method(*args, **kw) - - -def BuildDefaultGlobals(): - """ - Create a dictionary containing all the default globals for - SConstruct and SConscript files. - """ - - global GlobalDict - if GlobalDict is None: - GlobalDict = {} - - import SCons.Script - d = SCons.Script.__dict__ - def not_a_module(m, d=d, mtype=type(SCons.Script)): - return not isinstance(d[m], mtype) - for m in filter(not_a_module, dir(SCons.Script)): - GlobalDict[m] = d[m] - - return GlobalDict.copy() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Script/__init__.py b/scons/scons-local-2.2.0/SCons/Script/__init__.py deleted file mode 100644 index c0a031adb..000000000 --- a/scons/scons-local-2.2.0/SCons/Script/__init__.py +++ /dev/null @@ -1,412 +0,0 @@ -"""SCons.Script - -This file implements the main() function used by the scons script. - -Architecturally, this *is* the scons script, and will likely only be -called from the external "scons" wrapper. Consequently, anything here -should not be, or be considered, part of the build engine. If it's -something that we expect other software to want to use, it should go in -some other module. If it's specific to the "scons" script invocation, -it goes here. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Script/__init__.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import time -start_time = time.time() - -import collections -import os -import sys - -# Special chicken-and-egg handling of the "--debug=memoizer" flag: -# -# SCons.Memoize contains a metaclass implementation that affects how -# the other classes are instantiated. The Memoizer may add shim methods -# to classes that have methods that cache computed values in order to -# count and report the hits and misses. -# -# If we wait to enable the Memoization until after we've parsed the -# command line options normally, it will be too late, because the Memoizer -# will have already analyzed the classes that it's Memoizing and decided -# to not add the shims. So we use a special-case, up-front check for -# the "--debug=memoizer" flag and enable Memoizer before we import any -# of the other modules that use it. - -_args = sys.argv + os.environ.get('SCONSFLAGS', '').split() -if "--debug=memoizer" in _args: - import SCons.Memoize - import SCons.Warnings - try: - SCons.Memoize.EnableMemoization() - except SCons.Warnings.Warning: - # Some warning was thrown. Arrange for it to be displayed - # or not after warnings are configured. - import Main - exc_type, exc_value, tb = sys.exc_info() - Main.delayed_warnings.append((exc_type, exc_value)) -del _args - -import SCons.Action -import SCons.Builder -import SCons.Environment -import SCons.Node.FS -import SCons.Options -import SCons.Platform -import SCons.Scanner -import SCons.SConf -import SCons.Subst -import SCons.Tool -import SCons.Util -import SCons.Variables -import SCons.Defaults - -import Main - -main = Main.main - -# The following are global class definitions and variables that used to -# live directly in this module back before 0.96.90, when it contained -# a lot of code. Some SConscript files in widely-distributed packages -# (Blender is the specific example) actually reached into SCons.Script -# directly to use some of these. Rather than break those SConscript -# files, we're going to propagate these names into the SCons.Script -# namespace here. -# -# Some of these are commented out because it's *really* unlikely anyone -# used them, but we're going to leave the comment here to try to make -# it obvious what to do if the situation arises. -BuildTask = Main.BuildTask -CleanTask = Main.CleanTask -QuestionTask = Main.QuestionTask -#PrintHelp = Main.PrintHelp -#SConscriptSettableOptions = Main.SConscriptSettableOptions - -AddOption = Main.AddOption -GetOption = Main.GetOption -SetOption = Main.SetOption -Progress = Main.Progress -GetBuildFailures = Main.GetBuildFailures - -#keep_going_on_error = Main.keep_going_on_error -#print_dtree = Main.print_dtree -#print_explanations = Main.print_explanations -#print_includes = Main.print_includes -#print_objects = Main.print_objects -#print_time = Main.print_time -#print_tree = Main.print_tree -#memory_stats = Main.memory_stats -#ignore_errors = Main.ignore_errors -#sconscript_time = Main.sconscript_time -#command_time = Main.command_time -#exit_status = Main.exit_status -#profiling = Main.profiling -#repositories = Main.repositories - -# -import SConscript -_SConscript = SConscript - -call_stack = _SConscript.call_stack - -# -Action = SCons.Action.Action -AddMethod = SCons.Util.AddMethod -AllowSubstExceptions = SCons.Subst.SetAllowableExceptions -Builder = SCons.Builder.Builder -Configure = _SConscript.Configure -Environment = SCons.Environment.Environment -#OptParser = SCons.SConsOptions.OptParser -FindPathDirs = SCons.Scanner.FindPathDirs -Platform = SCons.Platform.Platform -Return = _SConscript.Return -Scanner = SCons.Scanner.Base -Tool = SCons.Tool.Tool -WhereIs = SCons.Util.WhereIs - -# -BoolVariable = SCons.Variables.BoolVariable -EnumVariable = SCons.Variables.EnumVariable -ListVariable = SCons.Variables.ListVariable -PackageVariable = SCons.Variables.PackageVariable -PathVariable = SCons.Variables.PathVariable - -# Deprecated names that will go away some day. -BoolOption = SCons.Options.BoolOption -EnumOption = SCons.Options.EnumOption -ListOption = SCons.Options.ListOption -PackageOption = SCons.Options.PackageOption -PathOption = SCons.Options.PathOption - -# Action factories. -Chmod = SCons.Defaults.Chmod -Copy = SCons.Defaults.Copy -Delete = SCons.Defaults.Delete -Mkdir = SCons.Defaults.Mkdir -Move = SCons.Defaults.Move -Touch = SCons.Defaults.Touch - -# Pre-made, public scanners. -CScanner = SCons.Tool.CScanner -DScanner = SCons.Tool.DScanner -DirScanner = SCons.Defaults.DirScanner -ProgramScanner = SCons.Tool.ProgramScanner -SourceFileScanner = SCons.Tool.SourceFileScanner - -# Functions we might still convert to Environment methods. -CScan = SCons.Defaults.CScan -DefaultEnvironment = SCons.Defaults.DefaultEnvironment - -# Other variables we provide. -class TargetList(collections.UserList): - def _do_nothing(self, *args, **kw): - pass - def _add_Default(self, list): - self.extend(list) - def _clear(self): - del self[:] - -ARGUMENTS = {} -ARGLIST = [] -BUILD_TARGETS = TargetList() -COMMAND_LINE_TARGETS = [] -DEFAULT_TARGETS = [] - -# BUILD_TARGETS can be modified in the SConscript files. If so, we -# want to treat the modified BUILD_TARGETS list as if they specified -# targets on the command line. To do that, though, we need to know if -# BUILD_TARGETS was modified through "official" APIs or by hand. We do -# this by updating two lists in parallel, the documented BUILD_TARGETS -# list, above, and this internal _build_plus_default targets list which -# should only have "official" API changes. Then Script/Main.py can -# compare these two afterwards to figure out if the user added their -# own targets to BUILD_TARGETS. -_build_plus_default = TargetList() - -def _Add_Arguments(alist): - for arg in alist: - a, b = arg.split('=', 1) - ARGUMENTS[a] = b - ARGLIST.append((a, b)) - -def _Add_Targets(tlist): - if tlist: - COMMAND_LINE_TARGETS.extend(tlist) - BUILD_TARGETS.extend(tlist) - BUILD_TARGETS._add_Default = BUILD_TARGETS._do_nothing - BUILD_TARGETS._clear = BUILD_TARGETS._do_nothing - _build_plus_default.extend(tlist) - _build_plus_default._add_Default = _build_plus_default._do_nothing - _build_plus_default._clear = _build_plus_default._do_nothing - -def _Set_Default_Targets_Has_Been_Called(d, fs): - return DEFAULT_TARGETS - -def _Set_Default_Targets_Has_Not_Been_Called(d, fs): - if d is None: - d = [fs.Dir('.')] - return d - -_Get_Default_Targets = _Set_Default_Targets_Has_Not_Been_Called - -def _Set_Default_Targets(env, tlist): - global DEFAULT_TARGETS - global _Get_Default_Targets - _Get_Default_Targets = _Set_Default_Targets_Has_Been_Called - for t in tlist: - if t is None: - # Delete the elements from the list in-place, don't - # reassign an empty list to DEFAULT_TARGETS, so that the - # variables will still point to the same object we point to. - del DEFAULT_TARGETS[:] - BUILD_TARGETS._clear() - _build_plus_default._clear() - elif isinstance(t, SCons.Node.Node): - DEFAULT_TARGETS.append(t) - BUILD_TARGETS._add_Default([t]) - _build_plus_default._add_Default([t]) - else: - nodes = env.arg2nodes(t, env.fs.Entry) - DEFAULT_TARGETS.extend(nodes) - BUILD_TARGETS._add_Default(nodes) - _build_plus_default._add_Default(nodes) - -# -help_text = None - -def HelpFunction(text): - global help_text - if SCons.Script.help_text is None: - SCons.Script.help_text = text - else: - help_text = help_text + text - -# -# Will be non-zero if we are reading an SConscript file. -sconscript_reading = 0 - -# -def Variables(files=[], args=ARGUMENTS): - return SCons.Variables.Variables(files, args) - -def Options(files=[], args=ARGUMENTS): - return SCons.Options.Options(files, args) - -# The list of global functions to add to the SConscript name space -# that end up calling corresponding methods or Builders in the -# DefaultEnvironment(). -GlobalDefaultEnvironmentFunctions = [ - # Methods from the SConsEnvironment class, above. - 'Default', - 'EnsurePythonVersion', - 'EnsureSConsVersion', - 'Exit', - 'Export', - 'GetLaunchDir', - 'Help', - 'Import', - #'SConscript', is handled separately, below. - 'SConscriptChdir', - - # Methods from the Environment.Base class. - 'AddPostAction', - 'AddPreAction', - 'Alias', - 'AlwaysBuild', - 'BuildDir', - 'CacheDir', - 'Clean', - #The Command() method is handled separately, below. - 'Decider', - 'Depends', - 'Dir', - 'NoClean', - 'NoCache', - 'Entry', - 'Execute', - 'File', - 'FindFile', - 'FindInstalledFiles', - 'FindSourceFiles', - 'Flatten', - 'GetBuildPath', - 'Glob', - 'Ignore', - 'Install', - 'InstallAs', - 'Literal', - 'Local', - 'ParseDepends', - 'Precious', - 'Repository', - 'Requires', - 'SConsignFile', - 'SideEffect', - 'SourceCode', - 'SourceSignatures', - 'Split', - 'Tag', - 'TargetSignatures', - 'Value', - 'VariantDir', -] - -GlobalDefaultBuilders = [ - # Supported builders. - 'CFile', - 'CXXFile', - 'DVI', - 'Jar', - 'Java', - 'JavaH', - 'Library', - 'M4', - 'MSVSProject', - 'Object', - 'PCH', - 'PDF', - 'PostScript', - 'Program', - 'RES', - 'RMIC', - 'SharedLibrary', - 'SharedObject', - 'StaticLibrary', - 'StaticObject', - 'Tar', - 'TypeLibrary', - 'Zip', - 'Package', -] - -for name in GlobalDefaultEnvironmentFunctions + GlobalDefaultBuilders: - exec "%s = _SConscript.DefaultEnvironmentCall(%s)" % (name, repr(name)) -del name - -# There are a handful of variables that used to live in the -# Script/SConscript.py module that some SConscript files out there were -# accessing directly as SCons.Script.SConscript.*. The problem is that -# "SConscript" in this namespace is no longer a module, it's a global -# function call--or more precisely, an object that implements a global -# function call through the default Environment. Nevertheless, we can -# maintain backwards compatibility for SConscripts that were reaching in -# this way by hanging some attributes off the "SConscript" object here. -SConscript = _SConscript.DefaultEnvironmentCall('SConscript') - -# Make SConscript look enough like the module it used to be so -# that pychecker doesn't barf. -SConscript.__name__ = 'SConscript' - -SConscript.Arguments = ARGUMENTS -SConscript.ArgList = ARGLIST -SConscript.BuildTargets = BUILD_TARGETS -SConscript.CommandLineTargets = COMMAND_LINE_TARGETS -SConscript.DefaultTargets = DEFAULT_TARGETS - -# The global Command() function must be handled differently than the -# global functions for other construction environment methods because -# we want people to be able to use Actions that must expand $TARGET -# and $SOURCE later, when (and if) the Action is invoked to build -# the target(s). We do this with the subst=1 argument, which creates -# a DefaultEnvironmentCall instance that wraps up a normal default -# construction environment that performs variable substitution, not a -# proxy that doesn't. -# -# There's a flaw here, though, because any other $-variables on a command -# line will *also* be expanded, each to a null string, but that should -# only be a problem in the unusual case where someone was passing a '$' -# on a command line and *expected* the $ to get through to the shell -# because they were calling Command() and not env.Command()... This is -# unlikely enough that we're going to leave this as is and cross that -# bridge if someone actually comes to it. -Command = _SConscript.DefaultEnvironmentCall('Command', subst=1) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Sig.py b/scons/scons-local-2.2.0/SCons/Sig.py deleted file mode 100644 index 41289a0f6..000000000 --- a/scons/scons-local-2.2.0/SCons/Sig.py +++ /dev/null @@ -1,63 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Sig.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -__doc__ = """Place-holder for the old SCons.Sig module hierarchy - -This is no longer used, but code out there (such as the NSIS module on -the SCons wiki) may try to import SCons.Sig. If so, we generate a warning -that points them to the line that caused the import, and don't die. - -If someone actually tried to use the sub-modules or functions within -the package (for example, SCons.Sig.MD5.signature()), then they'll still -get an AttributeError, but at least they'll know where to start looking. -""" - -import SCons.Util -import SCons.Warnings - -msg = 'The SCons.Sig module no longer exists.\n' \ - ' Remove the following "import SCons.Sig" line to eliminate this warning:' - -SCons.Warnings.warn(SCons.Warnings.DeprecatedSigModuleWarning, msg) - -default_calc = None -default_module = None - -class MD5Null(SCons.Util.Null): - def __repr__(self): - return "MD5Null()" - -class TimeStampNull(SCons.Util.Null): - def __repr__(self): - return "TimeStampNull()" - -MD5 = MD5Null() -TimeStamp = TimeStampNull() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Subst.py b/scons/scons-local-2.2.0/SCons/Subst.py deleted file mode 100644 index 8d9d3a43a..000000000 --- a/scons/scons-local-2.2.0/SCons/Subst.py +++ /dev/null @@ -1,904 +0,0 @@ -"""SCons.Subst - -SCons string substitution. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Subst.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import collections -import re - -import SCons.Errors - -from SCons.Util import is_String, is_Sequence - -# Indexed by the SUBST_* constants below. -_strconv = [SCons.Util.to_String_for_subst, - SCons.Util.to_String_for_subst, - SCons.Util.to_String_for_signature] - - - -AllowableExceptions = (IndexError, NameError) - -def SetAllowableExceptions(*excepts): - global AllowableExceptions - AllowableExceptions = [_f for _f in excepts if _f] - -def raise_exception(exception, target, s): - name = exception.__class__.__name__ - msg = "%s `%s' trying to evaluate `%s'" % (name, exception, s) - if target: - raise SCons.Errors.BuildError(target[0], msg) - else: - raise SCons.Errors.UserError(msg) - - - -class Literal(object): - """A wrapper for a string. If you use this object wrapped - around a string, then it will be interpreted as literal. - When passed to the command interpreter, all special - characters will be escaped.""" - def __init__(self, lstr): - self.lstr = lstr - - def __str__(self): - return self.lstr - - def escape(self, escape_func): - return escape_func(self.lstr) - - def for_signature(self): - return self.lstr - - def is_literal(self): - return 1 - -class SpecialAttrWrapper(object): - """This is a wrapper for what we call a 'Node special attribute.' - This is any of the attributes of a Node that we can reference from - Environment variable substitution, such as $TARGET.abspath or - $SOURCES[1].filebase. We implement the same methods as Literal - so we can handle special characters, plus a for_signature method, - such that we can return some canonical string during signature - calculation to avoid unnecessary rebuilds.""" - - def __init__(self, lstr, for_signature=None): - """The for_signature parameter, if supplied, will be the - canonical string we return from for_signature(). Else - we will simply return lstr.""" - self.lstr = lstr - if for_signature: - self.forsig = for_signature - else: - self.forsig = lstr - - def __str__(self): - return self.lstr - - def escape(self, escape_func): - return escape_func(self.lstr) - - def for_signature(self): - return self.forsig - - def is_literal(self): - return 1 - -def quote_spaces(arg): - """Generic function for putting double quotes around any string that - has white space in it.""" - if ' ' in arg or '\t' in arg: - return '"%s"' % arg - else: - return str(arg) - -class CmdStringHolder(collections.UserString): - """This is a special class used to hold strings generated by - scons_subst() and scons_subst_list(). It defines a special method - escape(). When passed a function with an escape algorithm for a - particular platform, it will return the contained string with the - proper escape sequences inserted. - """ - def __init__(self, cmd, literal=None): - collections.UserString.__init__(self, cmd) - self.literal = literal - - def is_literal(self): - return self.literal - - def escape(self, escape_func, quote_func=quote_spaces): - """Escape the string with the supplied function. The - function is expected to take an arbitrary string, then - return it with all special characters escaped and ready - for passing to the command interpreter. - - After calling this function, the next call to str() will - return the escaped string. - """ - - if self.is_literal(): - return escape_func(self.data) - elif ' ' in self.data or '\t' in self.data: - return quote_func(self.data) - else: - return self.data - -def escape_list(mylist, escape_func): - """Escape a list of arguments by running the specified escape_func - on every object in the list that has an escape() method.""" - def escape(obj, escape_func=escape_func): - try: - e = obj.escape - except AttributeError: - return obj - else: - return e(escape_func) - return list(map(escape, mylist)) - -class NLWrapper(object): - """A wrapper class that delays turning a list of sources or targets - into a NodeList until it's needed. The specified function supplied - when the object is initialized is responsible for turning raw nodes - into proxies that implement the special attributes like .abspath, - .source, etc. This way, we avoid creating those proxies just - "in case" someone is going to use $TARGET or the like, and only - go through the trouble if we really have to. - - In practice, this might be a wash performance-wise, but it's a little - cleaner conceptually... - """ - - def __init__(self, list, func): - self.list = list - self.func = func - def _return_nodelist(self): - return self.nodelist - def _gen_nodelist(self): - mylist = self.list - if mylist is None: - mylist = [] - elif not is_Sequence(mylist): - mylist = [mylist] - # The map(self.func) call is what actually turns - # a list into appropriate proxies. - self.nodelist = SCons.Util.NodeList(list(map(self.func, mylist))) - self._create_nodelist = self._return_nodelist - return self.nodelist - _create_nodelist = _gen_nodelist - - -class Targets_or_Sources(collections.UserList): - """A class that implements $TARGETS or $SOURCES expansions by in turn - wrapping a NLWrapper. This class handles the different methods used - to access the list, calling the NLWrapper to create proxies on demand. - - Note that we subclass collections.UserList purely so that the - is_Sequence() function will identify an object of this class as - a list during variable expansion. We're not really using any - collections.UserList methods in practice. - """ - def __init__(self, nl): - self.nl = nl - def __getattr__(self, attr): - nl = self.nl._create_nodelist() - return getattr(nl, attr) - def __getitem__(self, i): - nl = self.nl._create_nodelist() - return nl[i] - def __getslice__(self, i, j): - nl = self.nl._create_nodelist() - i = max(i, 0); j = max(j, 0) - return nl[i:j] - def __str__(self): - nl = self.nl._create_nodelist() - return str(nl) - def __repr__(self): - nl = self.nl._create_nodelist() - return repr(nl) - -class Target_or_Source(object): - """A class that implements $TARGET or $SOURCE expansions by in turn - wrapping a NLWrapper. This class handles the different methods used - to access an individual proxy Node, calling the NLWrapper to create - a proxy on demand. - """ - def __init__(self, nl): - self.nl = nl - def __getattr__(self, attr): - nl = self.nl._create_nodelist() - try: - nl0 = nl[0] - except IndexError: - # If there is nothing in the list, then we have no attributes to - # pass through, so raise AttributeError for everything. - raise AttributeError("NodeList has no attribute: %s" % attr) - return getattr(nl0, attr) - def __str__(self): - nl = self.nl._create_nodelist() - if nl: - return str(nl[0]) - return '' - def __repr__(self): - nl = self.nl._create_nodelist() - if nl: - return repr(nl[0]) - return '' - -class NullNodeList(SCons.Util.NullSeq): - def __call__(self, *args, **kwargs): return '' - def __str__(self): return '' - -NullNodesList = NullNodeList() - -def subst_dict(target, source): - """Create a dictionary for substitution of special - construction variables. - - This translates the following special arguments: - - target - the target (object or array of objects), - used to generate the TARGET and TARGETS - construction variables - - source - the source (object or array of objects), - used to generate the SOURCES and SOURCE - construction variables - """ - dict = {} - - if target: - def get_tgt_subst_proxy(thing): - try: - subst_proxy = thing.get_subst_proxy() - except AttributeError: - subst_proxy = thing # probably a string, just return it - return subst_proxy - tnl = NLWrapper(target, get_tgt_subst_proxy) - dict['TARGETS'] = Targets_or_Sources(tnl) - dict['TARGET'] = Target_or_Source(tnl) - - # This is a total cheat, but hopefully this dictionary goes - # away soon anyway. We just let these expand to $TARGETS - # because that's "good enough" for the use of ToolSurrogates - # (see test/ToolSurrogate.py) to generate documentation. - dict['CHANGED_TARGETS'] = '$TARGETS' - dict['UNCHANGED_TARGETS'] = '$TARGETS' - else: - dict['TARGETS'] = NullNodesList - dict['TARGET'] = NullNodesList - - if source: - def get_src_subst_proxy(node): - try: - rfile = node.rfile - except AttributeError: - pass - else: - node = rfile() - try: - return node.get_subst_proxy() - except AttributeError: - return node # probably a String, just return it - snl = NLWrapper(source, get_src_subst_proxy) - dict['SOURCES'] = Targets_or_Sources(snl) - dict['SOURCE'] = Target_or_Source(snl) - - # This is a total cheat, but hopefully this dictionary goes - # away soon anyway. We just let these expand to $TARGETS - # because that's "good enough" for the use of ToolSurrogates - # (see test/ToolSurrogate.py) to generate documentation. - dict['CHANGED_SOURCES'] = '$SOURCES' - dict['UNCHANGED_SOURCES'] = '$SOURCES' - else: - dict['SOURCES'] = NullNodesList - dict['SOURCE'] = NullNodesList - - return dict - -# Constants for the "mode" parameter to scons_subst_list() and -# scons_subst(). SUBST_RAW gives the raw command line. SUBST_CMD -# gives a command line suitable for passing to a shell. SUBST_SIG -# gives a command line appropriate for calculating the signature -# of a command line...if this changes, we should rebuild. -SUBST_CMD = 0 -SUBST_RAW = 1 -SUBST_SIG = 2 - -_rm = re.compile(r'\$[()]') -_remove = re.compile(r'\$\([^\$]*(\$[^\)][^\$]*)*\$\)') - -# Indexed by the SUBST_* constants above. -_regex_remove = [ _rm, None, _remove ] - -def _rm_list(list): - #return [ l for l in list if not l in ('$(', '$)') ] - return [l for l in list if not l in ('$(', '$)')] - -def _remove_list(list): - result = [] - do_append = result.append - for l in list: - if l == '$(': - do_append = lambda x: None - elif l == '$)': - do_append = result.append - else: - do_append(l) - return result - -# Indexed by the SUBST_* constants above. -_list_remove = [ _rm_list, None, _remove_list ] - -# Regular expressions for splitting strings and handling substitutions, -# for use by the scons_subst() and scons_subst_list() functions: -# -# The first expression compiled matches all of the $-introduced tokens -# that we need to process in some way, and is used for substitutions. -# The expressions it matches are: -# -# "$$" -# "$(" -# "$)" -# "$variable" [must begin with alphabetic or underscore] -# "${any stuff}" -# -# The second expression compiled is used for splitting strings into tokens -# to be processed, and it matches all of the tokens listed above, plus -# the following that affect how arguments do or don't get joined together: -# -# " " [white space] -# "non-white-space" [without any dollar signs] -# "$" [single dollar sign] -# -_dollar_exps_str = r'\$[\$\(\)]|\$[_a-zA-Z][\.\w]*|\${[^}]*}' -_dollar_exps = re.compile(r'(%s)' % _dollar_exps_str) -_separate_args = re.compile(r'(%s|\s+|[^\s\$]+|\$)' % _dollar_exps_str) - -# This regular expression is used to replace strings of multiple white -# space characters in the string result from the scons_subst() function. -_space_sep = re.compile(r'[\t ]+(?![^{]*})') - -def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None): - """Expand a string or list containing construction variable - substitutions. - - This is the work-horse function for substitutions in file names - and the like. The companion scons_subst_list() function (below) - handles separating command lines into lists of arguments, so see - that function if that's what you're looking for. - """ - if isinstance(strSubst, str) and strSubst.find('$') < 0: - return strSubst - - class StringSubber(object): - """A class to construct the results of a scons_subst() call. - - This binds a specific construction environment, mode, target and - source with two methods (substitute() and expand()) that handle - the expansion. - """ - def __init__(self, env, mode, conv, gvars): - self.env = env - self.mode = mode - self.conv = conv - self.gvars = gvars - - def expand(self, s, lvars): - """Expand a single "token" as necessary, returning an - appropriate string containing the expansion. - - This handles expanding different types of things (strings, - lists, callables) appropriately. It calls the wrapper - substitute() method to re-expand things as necessary, so that - the results of expansions of side-by-side strings still get - re-evaluated separately, not smushed together. - """ - if is_String(s): - try: - s0, s1 = s[:2] - except (IndexError, ValueError): - return s - if s0 != '$': - return s - if s1 == '$': - return '$' - elif s1 in '()': - return s - else: - key = s[1:] - if key[0] == '{' or key.find('.') >= 0: - if key[0] == '{': - key = key[1:-1] - try: - s = eval(key, self.gvars, lvars) - except KeyboardInterrupt: - raise - except Exception, e: - if e.__class__ in AllowableExceptions: - return '' - raise_exception(e, lvars['TARGETS'], s) - else: - if key in lvars: - s = lvars[key] - elif key in self.gvars: - s = self.gvars[key] - elif not NameError in AllowableExceptions: - raise_exception(NameError(key), lvars['TARGETS'], s) - else: - return '' - - # Before re-expanding the result, handle - # recursive expansion by copying the local - # variable dictionary and overwriting a null - # string for the value of the variable name - # we just expanded. - # - # This could potentially be optimized by only - # copying lvars when s contains more expansions, - # but lvars is usually supposed to be pretty - # small, and deeply nested variable expansions - # are probably more the exception than the norm, - # so it should be tolerable for now. - lv = lvars.copy() - var = key.split('.')[0] - lv[var] = '' - return self.substitute(s, lv) - elif is_Sequence(s): - def func(l, conv=self.conv, substitute=self.substitute, lvars=lvars): - return conv(substitute(l, lvars)) - return list(map(func, s)) - elif callable(s): - try: - s = s(target=lvars['TARGETS'], - source=lvars['SOURCES'], - env=self.env, - for_signature=(self.mode != SUBST_CMD)) - except TypeError: - # This probably indicates that it's a callable - # object that doesn't match our calling arguments - # (like an Action). - if self.mode == SUBST_RAW: - return s - s = self.conv(s) - return self.substitute(s, lvars) - elif s is None: - return '' - else: - return s - - def substitute(self, args, lvars): - """Substitute expansions in an argument or list of arguments. - - This serves as a wrapper for splitting up a string into - separate tokens. - """ - if is_String(args) and not isinstance(args, CmdStringHolder): - args = str(args) # In case it's a UserString. - try: - def sub_match(match): - return self.conv(self.expand(match.group(1), lvars)) - result = _dollar_exps.sub(sub_match, args) - except TypeError: - # If the internal conversion routine doesn't return - # strings (it could be overridden to return Nodes, for - # example), then the 1.5.2 re module will throw this - # exception. Back off to a slower, general-purpose - # algorithm that works for all data types. - args = _separate_args.findall(args) - result = [] - for a in args: - result.append(self.conv(self.expand(a, lvars))) - if len(result) == 1: - result = result[0] - else: - result = ''.join(map(str, result)) - return result - else: - return self.expand(args, lvars) - - if conv is None: - conv = _strconv[mode] - - # Doing this every time is a bit of a waste, since the Executor - # has typically already populated the OverrideEnvironment with - # $TARGET/$SOURCE variables. We're keeping this (for now), though, - # because it supports existing behavior that allows us to call - # an Action directly with an arbitrary target+source pair, which - # we use in Tool/tex.py to handle calling $BIBTEX when necessary. - # If we dropped that behavior (or found another way to cover it), - # we could get rid of this call completely and just rely on the - # Executor setting the variables. - if 'TARGET' not in lvars: - d = subst_dict(target, source) - if d: - lvars = lvars.copy() - lvars.update(d) - - # We're (most likely) going to eval() things. If Python doesn't - # find a __builtins__ value in the global dictionary used for eval(), - # it copies the current global values for you. Avoid this by - # setting it explicitly and then deleting, so we don't pollute the - # construction environment Dictionary(ies) that are typically used - # for expansion. - gvars['__builtins__'] = __builtins__ - - ss = StringSubber(env, mode, conv, gvars) - result = ss.substitute(strSubst, lvars) - - try: - del gvars['__builtins__'] - except KeyError: - pass - - if is_String(result): - # Remove $(-$) pairs and any stuff in between, - # if that's appropriate. - remove = _regex_remove[mode] - if remove: - result = remove.sub('', result) - if mode != SUBST_RAW: - # Compress strings of white space characters into - # a single space. - result = _space_sep.sub(' ', result).strip() - elif is_Sequence(result): - remove = _list_remove[mode] - if remove: - result = remove(result) - - return result - -#Subst_List_Strings = {} - -def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None): - """Substitute construction variables in a string (or list or other - object) and separate the arguments into a command list. - - The companion scons_subst() function (above) handles basic - substitutions within strings, so see that function instead - if that's what you're looking for. - """ -# try: -# Subst_List_Strings[strSubst] = Subst_List_Strings[strSubst] + 1 -# except KeyError: -# Subst_List_Strings[strSubst] = 1 -# import SCons.Debug -# SCons.Debug.caller_trace(1) - class ListSubber(collections.UserList): - """A class to construct the results of a scons_subst_list() call. - - Like StringSubber, this class binds a specific construction - environment, mode, target and source with two methods - (substitute() and expand()) that handle the expansion. - - In addition, however, this class is used to track the state of - the result(s) we're gathering so we can do the appropriate thing - whenever we have to append another word to the result--start a new - line, start a new word, append to the current word, etc. We do - this by setting the "append" attribute to the right method so - that our wrapper methods only need ever call ListSubber.append(), - and the rest of the object takes care of doing the right thing - internally. - """ - def __init__(self, env, mode, conv, gvars): - collections.UserList.__init__(self, []) - self.env = env - self.mode = mode - self.conv = conv - self.gvars = gvars - - if self.mode == SUBST_RAW: - self.add_strip = lambda x: self.append(x) - else: - self.add_strip = lambda x: None - self.in_strip = None - self.next_line() - - def expand(self, s, lvars, within_list): - """Expand a single "token" as necessary, appending the - expansion to the current result. - - This handles expanding different types of things (strings, - lists, callables) appropriately. It calls the wrapper - substitute() method to re-expand things as necessary, so that - the results of expansions of side-by-side strings still get - re-evaluated separately, not smushed together. - """ - - if is_String(s): - try: - s0, s1 = s[:2] - except (IndexError, ValueError): - self.append(s) - return - if s0 != '$': - self.append(s) - return - if s1 == '$': - self.append('$') - elif s1 == '(': - self.open_strip('$(') - elif s1 == ')': - self.close_strip('$)') - else: - key = s[1:] - if key[0] == '{' or key.find('.') >= 0: - if key[0] == '{': - key = key[1:-1] - try: - s = eval(key, self.gvars, lvars) - except KeyboardInterrupt: - raise - except Exception, e: - if e.__class__ in AllowableExceptions: - return - raise_exception(e, lvars['TARGETS'], s) - else: - if key in lvars: - s = lvars[key] - elif key in self.gvars: - s = self.gvars[key] - elif not NameError in AllowableExceptions: - raise_exception(NameError(), lvars['TARGETS'], s) - else: - return - - # Before re-expanding the result, handle - # recursive expansion by copying the local - # variable dictionary and overwriting a null - # string for the value of the variable name - # we just expanded. - lv = lvars.copy() - var = key.split('.')[0] - lv[var] = '' - self.substitute(s, lv, 0) - self.this_word() - elif is_Sequence(s): - for a in s: - self.substitute(a, lvars, 1) - self.next_word() - elif callable(s): - try: - s = s(target=lvars['TARGETS'], - source=lvars['SOURCES'], - env=self.env, - for_signature=(self.mode != SUBST_CMD)) - except TypeError: - # This probably indicates that it's a callable - # object that doesn't match our calling arguments - # (like an Action). - if self.mode == SUBST_RAW: - self.append(s) - return - s = self.conv(s) - self.substitute(s, lvars, within_list) - elif s is None: - self.this_word() - else: - self.append(s) - - def substitute(self, args, lvars, within_list): - """Substitute expansions in an argument or list of arguments. - - This serves as a wrapper for splitting up a string into - separate tokens. - """ - - if is_String(args) and not isinstance(args, CmdStringHolder): - args = str(args) # In case it's a UserString. - args = _separate_args.findall(args) - for a in args: - if a[0] in ' \t\n\r\f\v': - if '\n' in a: - self.next_line() - elif within_list: - self.append(a) - else: - self.next_word() - else: - self.expand(a, lvars, within_list) - else: - self.expand(args, lvars, within_list) - - def next_line(self): - """Arrange for the next word to start a new line. This - is like starting a new word, except that we have to append - another line to the result.""" - collections.UserList.append(self, []) - self.next_word() - - def this_word(self): - """Arrange for the next word to append to the end of the - current last word in the result.""" - self.append = self.add_to_current_word - - def next_word(self): - """Arrange for the next word to start a new word.""" - self.append = self.add_new_word - - def add_to_current_word(self, x): - """Append the string x to the end of the current last word - in the result. If that is not possible, then just add - it as a new word. Make sure the entire concatenated string - inherits the object attributes of x (in particular, the - escape function) by wrapping it as CmdStringHolder.""" - - if not self.in_strip or self.mode != SUBST_SIG: - try: - current_word = self[-1][-1] - except IndexError: - self.add_new_word(x) - else: - # All right, this is a hack and it should probably - # be refactored out of existence in the future. - # The issue is that we want to smoosh words together - # and make one file name that gets escaped if - # we're expanding something like foo$EXTENSION, - # but we don't want to smoosh them together if - # it's something like >$TARGET, because then we'll - # treat the '>' like it's part of the file name. - # So for now, just hard-code looking for the special - # command-line redirection characters... - try: - last_char = str(current_word)[-1] - except IndexError: - last_char = '\0' - if last_char in '<>|': - self.add_new_word(x) - else: - y = current_word + x - - # We used to treat a word appended to a literal - # as a literal itself, but this caused problems - # with interpreting quotes around space-separated - # targets on command lines. Removing this makes - # none of the "substantive" end-to-end tests fail, - # so we'll take this out but leave it commented - # for now in case there's a problem not covered - # by the test cases and we need to resurrect this. - #literal1 = self.literal(self[-1][-1]) - #literal2 = self.literal(x) - y = self.conv(y) - if is_String(y): - #y = CmdStringHolder(y, literal1 or literal2) - y = CmdStringHolder(y, None) - self[-1][-1] = y - - def add_new_word(self, x): - if not self.in_strip or self.mode != SUBST_SIG: - literal = self.literal(x) - x = self.conv(x) - if is_String(x): - x = CmdStringHolder(x, literal) - self[-1].append(x) - self.append = self.add_to_current_word - - def literal(self, x): - try: - l = x.is_literal - except AttributeError: - return None - else: - return l() - - def open_strip(self, x): - """Handle the "open strip" $( token.""" - self.add_strip(x) - self.in_strip = 1 - - def close_strip(self, x): - """Handle the "close strip" $) token.""" - self.add_strip(x) - self.in_strip = None - - if conv is None: - conv = _strconv[mode] - - # Doing this every time is a bit of a waste, since the Executor - # has typically already populated the OverrideEnvironment with - # $TARGET/$SOURCE variables. We're keeping this (for now), though, - # because it supports existing behavior that allows us to call - # an Action directly with an arbitrary target+source pair, which - # we use in Tool/tex.py to handle calling $BIBTEX when necessary. - # If we dropped that behavior (or found another way to cover it), - # we could get rid of this call completely and just rely on the - # Executor setting the variables. - if 'TARGET' not in lvars: - d = subst_dict(target, source) - if d: - lvars = lvars.copy() - lvars.update(d) - - # We're (most likely) going to eval() things. If Python doesn't - # find a __builtins__ value in the global dictionary used for eval(), - # it copies the current global values for you. Avoid this by - # setting it explicitly and then deleting, so we don't pollute the - # construction environment Dictionary(ies) that are typically used - # for expansion. - gvars['__builtins__'] = __builtins__ - - ls = ListSubber(env, mode, conv, gvars) - ls.substitute(strSubst, lvars, 0) - - try: - del gvars['__builtins__'] - except KeyError: - pass - - return ls.data - -def scons_subst_once(strSubst, env, key): - """Perform single (non-recursive) substitution of a single - construction variable keyword. - - This is used when setting a variable when copying or overriding values - in an Environment. We want to capture (expand) the old value before - we override it, so people can do things like: - - env2 = env.Clone(CCFLAGS = '$CCFLAGS -g') - - We do this with some straightforward, brute-force code here... - """ - if isinstance(strSubst, str) and strSubst.find('$') < 0: - return strSubst - - matchlist = ['$' + key, '${' + key + '}'] - val = env.get(key, '') - def sub_match(match, val=val, matchlist=matchlist): - a = match.group(1) - if a in matchlist: - a = val - if is_Sequence(a): - return ' '.join(map(str, a)) - else: - return str(a) - - if is_Sequence(strSubst): - result = [] - for arg in strSubst: - if is_String(arg): - if arg in matchlist: - arg = val - if is_Sequence(arg): - result.extend(arg) - else: - result.append(arg) - else: - result.append(_dollar_exps.sub(sub_match, arg)) - else: - result.append(arg) - return result - elif is_String(strSubst): - return _dollar_exps.sub(sub_match, strSubst) - else: - return strSubst - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Taskmaster.py b/scons/scons-local-2.2.0/SCons/Taskmaster.py deleted file mode 100644 index cd95fb081..000000000 --- a/scons/scons-local-2.2.0/SCons/Taskmaster.py +++ /dev/null @@ -1,1032 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__doc__ = """ -Generic Taskmaster module for the SCons build engine. - -This module contains the primary interface(s) between a wrapping user -interface and the SCons build engine. There are two key classes here: - - Taskmaster - This is the main engine for walking the dependency graph and - calling things to decide what does or doesn't need to be built. - - Task - This is the base class for allowing a wrapping interface to - decide what does or doesn't actually need to be done. The - intention is for a wrapping interface to subclass this as - appropriate for different types of behavior it may need. - - The canonical example is the SCons native Python interface, - which has Task subclasses that handle its specific behavior, - like printing "`foo' is up to date" when a top-level target - doesn't need to be built, and handling the -c option by removing - targets as its "build" action. There is also a separate subclass - for suppressing this output when the -q option is used. - - The Taskmaster instantiates a Task object for each (set of) - target(s) that it decides need to be evaluated and/or built. -""" - -__revision__ = "src/engine/SCons/Taskmaster.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -from itertools import chain -import operator -import sys -import traceback - -import SCons.Errors -import SCons.Node -import SCons.Warnings - -StateString = SCons.Node.StateString -NODE_NO_STATE = SCons.Node.no_state -NODE_PENDING = SCons.Node.pending -NODE_EXECUTING = SCons.Node.executing -NODE_UP_TO_DATE = SCons.Node.up_to_date -NODE_EXECUTED = SCons.Node.executed -NODE_FAILED = SCons.Node.failed - -print_prepare = 0 # set by option --debug=prepare - -# A subsystem for recording stats about how different Nodes are handled by -# the main Taskmaster loop. There's no external control here (no need for -# a --debug= option); enable it by changing the value of CollectStats. - -CollectStats = None - -class Stats(object): - """ - A simple class for holding statistics about the disposition of a - Node by the Taskmaster. If we're collecting statistics, each Node - processed by the Taskmaster gets one of these attached, in which case - the Taskmaster records its decision each time it processes the Node. - (Ideally, that's just once per Node.) - """ - def __init__(self): - """ - Instantiates a Taskmaster.Stats object, initializing all - appropriate counters to zero. - """ - self.considered = 0 - self.already_handled = 0 - self.problem = 0 - self.child_failed = 0 - self.not_built = 0 - self.side_effects = 0 - self.build = 0 - -StatsNodes = [] - -fmt = "%(considered)3d "\ - "%(already_handled)3d " \ - "%(problem)3d " \ - "%(child_failed)3d " \ - "%(not_built)3d " \ - "%(side_effects)3d " \ - "%(build)3d " - -def dump_stats(): - for n in sorted(StatsNodes, key=lambda a: str(a)): - print (fmt % n.stats.__dict__) + str(n) - - - -class Task(object): - """ - Default SCons build engine task. - - This controls the interaction of the actual building of node - and the rest of the engine. - - This is expected to handle all of the normally-customizable - aspects of controlling a build, so any given application - *should* be able to do what it wants by sub-classing this - class and overriding methods as appropriate. If an application - needs to customze something by sub-classing Taskmaster (or - some other build engine class), we should first try to migrate - that functionality into this class. - - Note that it's generally a good idea for sub-classes to call - these methods explicitly to update state, etc., rather than - roll their own interaction with Taskmaster from scratch. - """ - def __init__(self, tm, targets, top, node): - self.tm = tm - self.targets = targets - self.top = top - self.node = node - self.exc_clear() - - def trace_message(self, method, node, description='node'): - fmt = '%-20s %s %s\n' - return fmt % (method + ':', description, self.tm.trace_node(node)) - - def display(self, message): - """ - Hook to allow the calling interface to display a message. - - This hook gets called as part of preparing a task for execution - (that is, a Node to be built). As part of figuring out what Node - should be built next, the actually target list may be altered, - along with a message describing the alteration. The calling - interface can subclass Task and provide a concrete implementation - of this method to see those messages. - """ - pass - - def prepare(self): - """ - Called just before the task is executed. - - This is mainly intended to give the target Nodes a chance to - unlink underlying files and make all necessary directories before - the Action is actually called to build the targets. - """ - global print_prepare - T = self.tm.trace - if T: T.write(self.trace_message(u'Task.prepare()', self.node)) - - # Now that it's the appropriate time, give the TaskMaster a - # chance to raise any exceptions it encountered while preparing - # this task. - self.exception_raise() - - if self.tm.message: - self.display(self.tm.message) - self.tm.message = None - - # Let the targets take care of any necessary preparations. - # This includes verifying that all of the necessary sources - # and dependencies exist, removing the target file(s), etc. - # - # As of April 2008, the get_executor().prepare() method makes - # sure that all of the aggregate sources necessary to build this - # Task's target(s) exist in one up-front check. The individual - # target t.prepare() methods check that each target's explicit - # or implicit dependencies exists, and also initialize the - # .sconsign info. - executor = self.targets[0].get_executor() - executor.prepare() - for t in executor.get_action_targets(): - if print_prepare: - print "Preparing target %s..."%t - for s in t.side_effects: - print "...with side-effect %s..."%s - t.prepare() - for s in t.side_effects: - if print_prepare: - print "...Preparing side-effect %s..."%s - s.prepare() - - def get_target(self): - """Fetch the target being built or updated by this task. - """ - return self.node - - def needs_execute(self): - # TODO(deprecate): "return True" is the old default behavior; - # change it to NotImplementedError (after running through the - # Deprecation Cycle) so the desired behavior is explicitly - # determined by which concrete subclass is used. - #raise NotImplementedError - msg = ('Taskmaster.Task is an abstract base class; instead of\n' - '\tusing it directly, ' - 'derive from it and override the abstract methods.') - SCons.Warnings.warn(SCons.Warnings.TaskmasterNeedsExecuteWarning, msg) - return True - - def execute(self): - """ - Called to execute the task. - - This method is called from multiple threads in a parallel build, - so only do thread safe stuff here. Do thread unsafe stuff in - prepare(), executed() or failed(). - """ - T = self.tm.trace - if T: T.write(self.trace_message(u'Task.execute()', self.node)) - - try: - cached_targets = [] - for t in self.targets: - if not t.retrieve_from_cache(): - break - cached_targets.append(t) - if len(cached_targets) < len(self.targets): - # Remove targets before building. It's possible that we - # partially retrieved targets from the cache, leaving - # them in read-only mode. That might cause the command - # to fail. - # - for t in cached_targets: - try: - t.fs.unlink(t.path) - except (IOError, OSError): - pass - self.targets[0].build() - else: - for t in cached_targets: - t.cached = 1 - except SystemExit: - exc_value = sys.exc_info()[1] - raise SCons.Errors.ExplicitExit(self.targets[0], exc_value.code) - except SCons.Errors.UserError: - raise - except SCons.Errors.BuildError: - raise - except Exception, e: - buildError = SCons.Errors.convert_to_BuildError(e) - buildError.node = self.targets[0] - buildError.exc_info = sys.exc_info() - raise buildError - - def executed_without_callbacks(self): - """ - Called when the task has been successfully executed - and the Taskmaster instance doesn't want to call - the Node's callback methods. - """ - T = self.tm.trace - if T: T.write(self.trace_message('Task.executed_without_callbacks()', - self.node)) - - for t in self.targets: - if t.get_state() == NODE_EXECUTING: - for side_effect in t.side_effects: - side_effect.set_state(NODE_NO_STATE) - t.set_state(NODE_EXECUTED) - - def executed_with_callbacks(self): - """ - Called when the task has been successfully executed and - the Taskmaster instance wants to call the Node's callback - methods. - - This may have been a do-nothing operation (to preserve build - order), so we must check the node's state before deciding whether - it was "built", in which case we call the appropriate Node method. - In any event, we always call "visited()", which will handle any - post-visit actions that must take place regardless of whether - or not the target was an actual built target or a source Node. - """ - T = self.tm.trace - if T: T.write(self.trace_message('Task.executed_with_callbacks()', - self.node)) - - for t in self.targets: - if t.get_state() == NODE_EXECUTING: - for side_effect in t.side_effects: - side_effect.set_state(NODE_NO_STATE) - t.set_state(NODE_EXECUTED) - if not t.cached: - t.push_to_cache() - t.built() - t.visited() - - executed = executed_with_callbacks - - def failed(self): - """ - Default action when a task fails: stop the build. - - Note: Although this function is normally invoked on nodes in - the executing state, it might also be invoked on up-to-date - nodes when using Configure(). - """ - self.fail_stop() - - def fail_stop(self): - """ - Explicit stop-the-build failure. - - This sets failure status on the target nodes and all of - their dependent parent nodes. - - Note: Although this function is normally invoked on nodes in - the executing state, it might also be invoked on up-to-date - nodes when using Configure(). - """ - T = self.tm.trace - if T: T.write(self.trace_message('Task.failed_stop()', self.node)) - - # Invoke will_not_build() to clean-up the pending children - # list. - self.tm.will_not_build(self.targets, lambda n: n.set_state(NODE_FAILED)) - - # Tell the taskmaster to not start any new tasks - self.tm.stop() - - # We're stopping because of a build failure, but give the - # calling Task class a chance to postprocess() the top-level - # target under which the build failure occurred. - self.targets = [self.tm.current_top] - self.top = 1 - - def fail_continue(self): - """ - Explicit continue-the-build failure. - - This sets failure status on the target nodes and all of - their dependent parent nodes. - - Note: Although this function is normally invoked on nodes in - the executing state, it might also be invoked on up-to-date - nodes when using Configure(). - """ - T = self.tm.trace - if T: T.write(self.trace_message('Task.failed_continue()', self.node)) - - self.tm.will_not_build(self.targets, lambda n: n.set_state(NODE_FAILED)) - - def make_ready_all(self): - """ - Marks all targets in a task ready for execution. - - This is used when the interface needs every target Node to be - visited--the canonical example being the "scons -c" option. - """ - T = self.tm.trace - if T: T.write(self.trace_message('Task.make_ready_all()', self.node)) - - self.out_of_date = self.targets[:] - for t in self.targets: - t.disambiguate().set_state(NODE_EXECUTING) - for s in t.side_effects: - # add disambiguate here to mirror the call on targets above - s.disambiguate().set_state(NODE_EXECUTING) - - def make_ready_current(self): - """ - Marks all targets in a task ready for execution if any target - is not current. - - This is the default behavior for building only what's necessary. - """ - T = self.tm.trace - if T: T.write(self.trace_message(u'Task.make_ready_current()', - self.node)) - - self.out_of_date = [] - needs_executing = False - for t in self.targets: - try: - t.disambiguate().make_ready() - is_up_to_date = not t.has_builder() or \ - (not t.always_build and t.is_up_to_date()) - except EnvironmentError, e: - raise SCons.Errors.BuildError(node=t, errstr=e.strerror, filename=e.filename) - - if not is_up_to_date: - self.out_of_date.append(t) - needs_executing = True - - if needs_executing: - for t in self.targets: - t.set_state(NODE_EXECUTING) - for s in t.side_effects: - # add disambiguate here to mirror the call on targets in first loop above - s.disambiguate().set_state(NODE_EXECUTING) - else: - for t in self.targets: - # We must invoke visited() to ensure that the node - # information has been computed before allowing the - # parent nodes to execute. (That could occur in a - # parallel build...) - t.visited() - t.set_state(NODE_UP_TO_DATE) - - make_ready = make_ready_current - - def postprocess(self): - """ - Post-processes a task after it's been executed. - - This examines all the targets just built (or not, we don't care - if the build was successful, or even if there was no build - because everything was up-to-date) to see if they have any - waiting parent Nodes, or Nodes waiting on a common side effect, - that can be put back on the candidates list. - """ - T = self.tm.trace - if T: T.write(self.trace_message(u'Task.postprocess()', self.node)) - - # We may have built multiple targets, some of which may have - # common parents waiting for this build. Count up how many - # targets each parent was waiting for so we can subtract the - # values later, and so we *don't* put waiting side-effect Nodes - # back on the candidates list if the Node is also a waiting - # parent. - - targets = set(self.targets) - - pending_children = self.tm.pending_children - parents = {} - for t in targets: - # A node can only be in the pending_children set if it has - # some waiting_parents. - if t.waiting_parents: - if T: T.write(self.trace_message(u'Task.postprocess()', - t, - 'removing')) - pending_children.discard(t) - for p in t.waiting_parents: - parents[p] = parents.get(p, 0) + 1 - - for t in targets: - for s in t.side_effects: - if s.get_state() == NODE_EXECUTING: - s.set_state(NODE_NO_STATE) - for p in s.waiting_parents: - parents[p] = parents.get(p, 0) + 1 - for p in s.waiting_s_e: - if p.ref_count == 0: - self.tm.candidates.append(p) - - for p, subtract in parents.items(): - p.ref_count = p.ref_count - subtract - if T: T.write(self.trace_message(u'Task.postprocess()', - p, - 'adjusted parent ref count')) - if p.ref_count == 0: - self.tm.candidates.append(p) - - for t in targets: - t.postprocess() - - # Exception handling subsystem. - # - # Exceptions that occur while walking the DAG or examining Nodes - # must be raised, but must be raised at an appropriate time and in - # a controlled manner so we can, if necessary, recover gracefully, - # possibly write out signature information for Nodes we've updated, - # etc. This is done by having the Taskmaster tell us about the - # exception, and letting - - def exc_info(self): - """ - Returns info about a recorded exception. - """ - return self.exception - - def exc_clear(self): - """ - Clears any recorded exception. - - This also changes the "exception_raise" attribute to point - to the appropriate do-nothing method. - """ - self.exception = (None, None, None) - self.exception_raise = self._no_exception_to_raise - - def exception_set(self, exception=None): - """ - Records an exception to be raised at the appropriate time. - - This also changes the "exception_raise" attribute to point - to the method that will, in fact - """ - if not exception: - exception = sys.exc_info() - self.exception = exception - self.exception_raise = self._exception_raise - - def _no_exception_to_raise(self): - pass - - def _exception_raise(self): - """ - Raises a pending exception that was recorded while getting a - Task ready for execution. - """ - exc = self.exc_info()[:] - try: - exc_type, exc_value, exc_traceback = exc - except ValueError: - exc_type, exc_value = exc - exc_traceback = None - raise exc_type, exc_value, exc_traceback - -class AlwaysTask(Task): - def needs_execute(self): - """ - Always returns True (indicating this Task should always - be executed). - - Subclasses that need this behavior (as opposed to the default - of only executing Nodes that are out of date w.r.t. their - dependencies) can use this as follows: - - class MyTaskSubclass(SCons.Taskmaster.Task): - needs_execute = SCons.Taskmaster.Task.execute_always - """ - return True - -class OutOfDateTask(Task): - def needs_execute(self): - """ - Returns True (indicating this Task should be executed) if this - Task's target state indicates it needs executing, which has - already been determined by an earlier up-to-date check. - """ - return self.targets[0].get_state() == SCons.Node.executing - - -def find_cycle(stack, visited): - if stack[-1] in visited: - return None - visited.add(stack[-1]) - for n in stack[-1].waiting_parents: - stack.append(n) - if stack[0] == stack[-1]: - return stack - if find_cycle(stack, visited): - return stack - stack.pop() - return None - - -class Taskmaster(object): - """ - The Taskmaster for walking the dependency DAG. - """ - - def __init__(self, targets=[], tasker=None, order=None, trace=None): - self.original_top = targets - self.top_targets_left = targets[:] - self.top_targets_left.reverse() - self.candidates = [] - if tasker is None: - tasker = OutOfDateTask - self.tasker = tasker - if not order: - order = lambda l: l - self.order = order - self.message = None - self.trace = trace - self.next_candidate = self.find_next_candidate - self.pending_children = set() - - def find_next_candidate(self): - """ - Returns the next candidate Node for (potential) evaluation. - - The candidate list (really a stack) initially consists of all of - the top-level (command line) targets provided when the Taskmaster - was initialized. While we walk the DAG, visiting Nodes, all the - children that haven't finished processing get pushed on to the - candidate list. Each child can then be popped and examined in - turn for whether *their* children are all up-to-date, in which - case a Task will be created for their actual evaluation and - potential building. - - Here is where we also allow candidate Nodes to alter the list of - Nodes that should be examined. This is used, for example, when - invoking SCons in a source directory. A source directory Node can - return its corresponding build directory Node, essentially saying, - "Hey, you really need to build this thing over here instead." - """ - try: - return self.candidates.pop() - except IndexError: - pass - try: - node = self.top_targets_left.pop() - except IndexError: - return None - self.current_top = node - alt, message = node.alter_targets() - if alt: - self.message = message - self.candidates.append(node) - self.candidates.extend(self.order(alt)) - node = self.candidates.pop() - return node - - def no_next_candidate(self): - """ - Stops Taskmaster processing by not returning a next candidate. - - Note that we have to clean-up the Taskmaster candidate list - because the cycle detection depends on the fact all nodes have - been processed somehow. - """ - while self.candidates: - candidates = self.candidates - self.candidates = [] - self.will_not_build(candidates) - return None - - def _validate_pending_children(self): - """ - Validate the content of the pending_children set. Assert if an - internal error is found. - - This function is used strictly for debugging the taskmaster by - checking that no invariants are violated. It is not used in - normal operation. - - The pending_children set is used to detect cycles in the - dependency graph. We call a "pending child" a child that is - found in the "pending" state when checking the dependencies of - its parent node. - - A pending child can occur when the Taskmaster completes a loop - through a cycle. For example, lets imagine a graph made of - three node (A, B and C) making a cycle. The evaluation starts - at node A. The taskmaster first consider whether node A's - child B is up-to-date. Then, recursively, node B needs to - check whether node C is up-to-date. This leaves us with a - dependency graph looking like: - - Next candidate \ - \ - Node A (Pending) --> Node B(Pending) --> Node C (NoState) - ^ | - | | - +-------------------------------------+ - - Now, when the Taskmaster examines the Node C's child Node A, - it finds that Node A is in the "pending" state. Therefore, - Node A is a pending child of node C. - - Pending children indicate that the Taskmaster has potentially - loop back through a cycle. We say potentially because it could - also occur when a DAG is evaluated in parallel. For example, - consider the following graph: - - - Node A (Pending) --> Node B(Pending) --> Node C (Pending) --> ... - | ^ - | | - +----------> Node D (NoState) --------+ - / - Next candidate / - - The Taskmaster first evaluates the nodes A, B, and C and - starts building some children of node C. Assuming, that the - maximum parallel level has not been reached, the Taskmaster - will examine Node D. It will find that Node C is a pending - child of Node D. - - In summary, evaluating a graph with a cycle will always - involve a pending child at one point. A pending child might - indicate either a cycle or a diamond-shaped DAG. Only a - fraction of the nodes ends-up being a "pending child" of - another node. This keeps the pending_children set small in - practice. - - We can differentiate between the two cases if we wait until - the end of the build. At this point, all the pending children - nodes due to a diamond-shaped DAG will have been properly - built (or will have failed to build). But, the pending - children involved in a cycle will still be in the pending - state. - - The taskmaster removes nodes from the pending_children set as - soon as a pending_children node moves out of the pending - state. This also helps to keep the pending_children set small. - """ - - for n in self.pending_children: - assert n.state in (NODE_PENDING, NODE_EXECUTING), \ - (str(n), StateString[n.state]) - assert len(n.waiting_parents) != 0, (str(n), len(n.waiting_parents)) - for p in n.waiting_parents: - assert p.ref_count > 0, (str(n), str(p), p.ref_count) - - - def trace_message(self, message): - return 'Taskmaster: %s\n' % message - - def trace_node(self, node): - return '<%-10s %-3s %s>' % (StateString[node.get_state()], - node.ref_count, - repr(str(node))) - - def _find_next_ready_node(self): - """ - Finds the next node that is ready to be built. - - This is *the* main guts of the DAG walk. We loop through the - list of candidates, looking for something that has no un-built - children (i.e., that is a leaf Node or has dependencies that are - all leaf Nodes or up-to-date). Candidate Nodes are re-scanned - (both the target Node itself and its sources, which are always - scanned in the context of a given target) to discover implicit - dependencies. A Node that must wait for some children to be - built will be put back on the candidates list after the children - have finished building. A Node that has been put back on the - candidates list in this way may have itself (or its sources) - re-scanned, in order to handle generated header files (e.g.) and - the implicit dependencies therein. - - Note that this method does not do any signature calculation or - up-to-date check itself. All of that is handled by the Task - class. This is purely concerned with the dependency graph walk. - """ - - self.ready_exc = None - - T = self.trace - if T: T.write(u'\n' + self.trace_message('Looking for a node to evaluate')) - - while True: - node = self.next_candidate() - if node is None: - if T: T.write(self.trace_message('No candidate anymore.') + u'\n') - return None - - node = node.disambiguate() - state = node.get_state() - - # For debugging only: - # - # try: - # self._validate_pending_children() - # except: - # self.ready_exc = sys.exc_info() - # return node - - if CollectStats: - if not hasattr(node, 'stats'): - node.stats = Stats() - StatsNodes.append(node) - S = node.stats - S.considered = S.considered + 1 - else: - S = None - - if T: T.write(self.trace_message(u' Considering node %s and its children:' % self.trace_node(node))) - - if state == NODE_NO_STATE: - # Mark this node as being on the execution stack: - node.set_state(NODE_PENDING) - elif state > NODE_PENDING: - # Skip this node if it has already been evaluated: - if S: S.already_handled = S.already_handled + 1 - if T: T.write(self.trace_message(u' already handled (executed)')) - continue - - executor = node.get_executor() - - try: - children = executor.get_all_children() - except SystemExit: - exc_value = sys.exc_info()[1] - e = SCons.Errors.ExplicitExit(node, exc_value.code) - self.ready_exc = (SCons.Errors.ExplicitExit, e) - if T: T.write(self.trace_message(' SystemExit')) - return node - except Exception, e: - # We had a problem just trying to figure out the - # children (like a child couldn't be linked in to a - # VariantDir, or a Scanner threw something). Arrange to - # raise the exception when the Task is "executed." - self.ready_exc = sys.exc_info() - if S: S.problem = S.problem + 1 - if T: T.write(self.trace_message(' exception %s while scanning children.\n' % e)) - return node - - children_not_visited = [] - children_pending = set() - children_not_ready = [] - children_failed = False - - for child in chain(executor.get_all_prerequisites(), children): - childstate = child.get_state() - - if T: T.write(self.trace_message(u' ' + self.trace_node(child))) - - if childstate == NODE_NO_STATE: - children_not_visited.append(child) - elif childstate == NODE_PENDING: - children_pending.add(child) - elif childstate == NODE_FAILED: - children_failed = True - - if childstate <= NODE_EXECUTING: - children_not_ready.append(child) - - - # These nodes have not even been visited yet. Add - # them to the list so that on some next pass we can - # take a stab at evaluating them (or their children). - children_not_visited.reverse() - self.candidates.extend(self.order(children_not_visited)) - #if T and children_not_visited: - # T.write(self.trace_message(' adding to candidates: %s' % map(str, children_not_visited))) - # T.write(self.trace_message(' candidates now: %s\n' % map(str, self.candidates))) - - # Skip this node if any of its children have failed. - # - # This catches the case where we're descending a top-level - # target and one of our children failed while trying to be - # built by a *previous* descent of an earlier top-level - # target. - # - # It can also occur if a node is reused in multiple - # targets. One first descends though the one of the - # target, the next time occurs through the other target. - # - # Note that we can only have failed_children if the - # --keep-going flag was used, because without it the build - # will stop before diving in the other branch. - # - # Note that even if one of the children fails, we still - # added the other children to the list of candidate nodes - # to keep on building (--keep-going). - if children_failed: - for n in executor.get_action_targets(): - n.set_state(NODE_FAILED) - - if S: S.child_failed = S.child_failed + 1 - if T: T.write(self.trace_message('****** %s\n' % self.trace_node(node))) - continue - - if children_not_ready: - for child in children_not_ready: - # We're waiting on one or more derived targets - # that have not yet finished building. - if S: S.not_built = S.not_built + 1 - - # Add this node to the waiting parents lists of - # anything we're waiting on, with a reference - # count so we can be put back on the list for - # re-evaluation when they've all finished. - node.ref_count = node.ref_count + child.add_to_waiting_parents(node) - if T: T.write(self.trace_message(u' adjusted ref count: %s, child %s' % - (self.trace_node(node), repr(str(child))))) - - if T: - for pc in children_pending: - T.write(self.trace_message(' adding %s to the pending children set\n' % - self.trace_node(pc))) - self.pending_children = self.pending_children | children_pending - - continue - - # Skip this node if it has side-effects that are - # currently being built: - wait_side_effects = False - for se in executor.get_action_side_effects(): - if se.get_state() == NODE_EXECUTING: - se.add_to_waiting_s_e(node) - wait_side_effects = True - - if wait_side_effects: - if S: S.side_effects = S.side_effects + 1 - continue - - # The default when we've gotten through all of the checks above: - # this node is ready to be built. - if S: S.build = S.build + 1 - if T: T.write(self.trace_message(u'Evaluating %s\n' % - self.trace_node(node))) - - # For debugging only: - # - # try: - # self._validate_pending_children() - # except: - # self.ready_exc = sys.exc_info() - # return node - - return node - - return None - - def next_task(self): - """ - Returns the next task to be executed. - - This simply asks for the next Node to be evaluated, and then wraps - it in the specific Task subclass with which we were initialized. - """ - node = self._find_next_ready_node() - - if node is None: - return None - - tlist = node.get_executor().get_all_targets() - - task = self.tasker(self, tlist, node in self.original_top, node) - try: - task.make_ready() - except: - # We had a problem just trying to get this task ready (like - # a child couldn't be linked in to a VariantDir when deciding - # whether this node is current). Arrange to raise the - # exception when the Task is "executed." - self.ready_exc = sys.exc_info() - - if self.ready_exc: - task.exception_set(self.ready_exc) - - self.ready_exc = None - - return task - - def will_not_build(self, nodes, node_func=lambda n: None): - """ - Perform clean-up about nodes that will never be built. Invokes - a user defined function on all of these nodes (including all - of their parents). - """ - - T = self.trace - - pending_children = self.pending_children - - to_visit = set(nodes) - pending_children = pending_children - to_visit - - if T: - for n in nodes: - T.write(self.trace_message(' removing node %s from the pending children set\n' % - self.trace_node(n))) - try: - while len(to_visit): - node = to_visit.pop() - node_func(node) - - # Prune recursion by flushing the waiting children - # list immediately. - parents = node.waiting_parents - node.waiting_parents = set() - - to_visit = to_visit | parents - pending_children = pending_children - parents - - for p in parents: - p.ref_count = p.ref_count - 1 - if T: T.write(self.trace_message(' removing parent %s from the pending children set\n' % - self.trace_node(p))) - except KeyError: - # The container to_visit has been emptied. - pass - - # We have the stick back the pending_children list into the - # taskmaster because the python 1.5.2 compatibility does not - # allow us to use in-place updates - self.pending_children = pending_children - - def stop(self): - """ - Stops the current build completely. - """ - self.next_candidate = self.no_next_candidate - - def cleanup(self): - """ - Check for dependency cycles. - """ - if not self.pending_children: - return - - nclist = [(n, find_cycle([n], set())) for n in self.pending_children] - - genuine_cycles = [ - node for node,cycle in nclist - if cycle or node.get_state() != NODE_EXECUTED - ] - if not genuine_cycles: - # All of the "cycles" found were single nodes in EXECUTED state, - # which is to say, they really weren't cycles. Just return. - return - - desc = 'Found dependency cycle(s):\n' - for node, cycle in nclist: - if cycle: - desc = desc + " " + " -> ".join(map(str, cycle)) + "\n" - else: - desc = desc + \ - " Internal Error: no cycle found for node %s (%s) in state %s\n" % \ - (node, repr(node), StateString[node.get_state()]) - - raise SCons.Errors.UserError(desc) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/386asm.py b/scons/scons-local-2.2.0/SCons/Tool/386asm.py deleted file mode 100644 index bf32a0b7d..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/386asm.py +++ /dev/null @@ -1,61 +0,0 @@ -"""SCons.Tool.386asm - -Tool specification for the 386ASM assembler for the Phar Lap ETS embedded -operating system. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/386asm.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -from SCons.Tool.PharLapCommon import addPharLapPaths -import SCons.Util - -as_module = __import__('as', globals(), locals(), []) - -def generate(env): - """Add Builders and construction variables for ar to an Environment.""" - as_module.generate(env) - - env['AS'] = '386asm' - env['ASFLAGS'] = SCons.Util.CLVar('') - env['ASPPFLAGS'] = '$ASFLAGS' - env['ASCOM'] = '$AS $ASFLAGS $SOURCES -o $TARGET' - env['ASPPCOM'] = '$CC $ASPPFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $SOURCES -o $TARGET' - - addPharLapPaths(env) - -def exists(env): - return env.Detect('386asm') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/BitKeeper.py b/scons/scons-local-2.2.0/SCons/Tool/BitKeeper.py deleted file mode 100644 index 60445db1e..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/BitKeeper.py +++ /dev/null @@ -1,67 +0,0 @@ -"""SCons.Tool.BitKeeper.py - -Tool-specific initialization for the BitKeeper source code control -system. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/BitKeeper.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Action -import SCons.Builder -import SCons.Util - -def generate(env): - """Add a Builder factory function and construction variables for - BitKeeper to an Environment.""" - - def BitKeeperFactory(env=env): - """ """ - import SCons.Warnings as W - W.warn(W.DeprecatedSourceCodeWarning, """The BitKeeper() factory is deprecated and there is no replacement.""") - act = SCons.Action.Action("$BITKEEPERCOM", "$BITKEEPERCOMSTR") - return SCons.Builder.Builder(action = act, env = env) - - #setattr(env, 'BitKeeper', BitKeeperFactory) - env.BitKeeper = BitKeeperFactory - - env['BITKEEPER'] = 'bk' - env['BITKEEPERGET'] = '$BITKEEPER get' - env['BITKEEPERGETFLAGS'] = SCons.Util.CLVar('') - env['BITKEEPERCOM'] = '$BITKEEPERGET $BITKEEPERGETFLAGS $TARGET' - -def exists(env): - return env.Detect('bk') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/CVS.py b/scons/scons-local-2.2.0/SCons/Tool/CVS.py deleted file mode 100644 index 87a6f1a32..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/CVS.py +++ /dev/null @@ -1,73 +0,0 @@ -"""SCons.Tool.CVS.py - -Tool-specific initialization for CVS. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Tool/CVS.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Action -import SCons.Builder -import SCons.Util - -def generate(env): - """Add a Builder factory function and construction variables for - CVS to an Environment.""" - - def CVSFactory(repos, module='', env=env): - """ """ - import SCons.Warnings as W - W.warn(W.DeprecatedSourceCodeWarning, """The CVS() factory is deprecated and there is no replacement.""") - # fail if repos is not an absolute path name? - if module != '': - # Don't use os.path.join() because the name we fetch might - # be across a network and must use POSIX slashes as separators. - module = module + '/' - env['CVSCOM'] = '$CVS $CVSFLAGS co $CVSCOFLAGS -d ${TARGET.dir} $CVSMODULE${TARGET.posix}' - act = SCons.Action.Action('$CVSCOM', '$CVSCOMSTR') - return SCons.Builder.Builder(action = act, - env = env, - CVSREPOSITORY = repos, - CVSMODULE = module) - - #setattr(env, 'CVS', CVSFactory) - env.CVS = CVSFactory - - env['CVS'] = 'cvs' - env['CVSFLAGS'] = SCons.Util.CLVar('-d $CVSREPOSITORY') - env['CVSCOFLAGS'] = SCons.Util.CLVar('') - env['CVSCOM'] = '$CVS $CVSFLAGS co $CVSCOFLAGS ${TARGET.posix}' - -def exists(env): - return env.Detect('cvs') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/FortranCommon.py b/scons/scons-local-2.2.0/SCons/Tool/FortranCommon.py deleted file mode 100644 index 2efcfa27b..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/FortranCommon.py +++ /dev/null @@ -1,263 +0,0 @@ -"""SCons.Tool.FortranCommon - -Stuff for processing Fortran, common to all fortran dialects. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/FortranCommon.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import re -import os.path - -import SCons.Action -import SCons.Defaults -import SCons.Scanner.Fortran -import SCons.Tool -import SCons.Util - -def isfortran(env, source): - """Return 1 if any of code in source has fortran files in it, 0 - otherwise.""" - try: - fsuffixes = env['FORTRANSUFFIXES'] - except KeyError: - # If no FORTRANSUFFIXES, no fortran tool, so there is no need to look - # for fortran sources. - return 0 - - if not source: - # Source might be None for unusual cases like SConf. - return 0 - for s in source: - if s.sources: - ext = os.path.splitext(str(s.sources[0]))[1] - if ext in fsuffixes: - return 1 - return 0 - -def _fortranEmitter(target, source, env): - node = source[0].rfile() - if not node.exists() and not node.is_derived(): - print "Could not locate " + str(node.name) - return ([], []) - mod_regex = """(?i)^\s*MODULE\s+(?!PROCEDURE)(\w+)""" - cre = re.compile(mod_regex,re.M) - # Retrieve all USE'd module names - modules = cre.findall(node.get_text_contents()) - # Remove unique items from the list - modules = SCons.Util.unique(modules) - # Convert module name to a .mod filename - suffix = env.subst('$FORTRANMODSUFFIX', target=target, source=source) - moddir = env.subst('$FORTRANMODDIR', target=target, source=source) - modules = [x.lower() + suffix for x in modules] - for m in modules: - target.append(env.fs.File(m, moddir)) - return (target, source) - -def FortranEmitter(target, source, env): - target, source = _fortranEmitter(target, source, env) - return SCons.Defaults.StaticObjectEmitter(target, source, env) - -def ShFortranEmitter(target, source, env): - target, source = _fortranEmitter(target, source, env) - return SCons.Defaults.SharedObjectEmitter(target, source, env) - -def ComputeFortranSuffixes(suffixes, ppsuffixes): - """suffixes are fortran source files, and ppsuffixes the ones to be - pre-processed. Both should be sequences, not strings.""" - assert len(suffixes) > 0 - s = suffixes[0] - sup = s.upper() - upper_suffixes = [_.upper() for _ in suffixes] - if SCons.Util.case_sensitive_suffixes(s, sup): - ppsuffixes.extend(upper_suffixes) - else: - suffixes.extend(upper_suffixes) - -def CreateDialectActions(dialect): - """Create dialect specific actions.""" - CompAction = SCons.Action.Action('$%sCOM ' % dialect, '$%sCOMSTR' % dialect) - CompPPAction = SCons.Action.Action('$%sPPCOM ' % dialect, '$%sPPCOMSTR' % dialect) - ShCompAction = SCons.Action.Action('$SH%sCOM ' % dialect, '$SH%sCOMSTR' % dialect) - ShCompPPAction = SCons.Action.Action('$SH%sPPCOM ' % dialect, '$SH%sPPCOMSTR' % dialect) - - return CompAction, CompPPAction, ShCompAction, ShCompPPAction - -def DialectAddToEnv(env, dialect, suffixes, ppsuffixes, support_module = 0): - """Add dialect specific construction variables.""" - ComputeFortranSuffixes(suffixes, ppsuffixes) - - fscan = SCons.Scanner.Fortran.FortranScan("%sPATH" % dialect) - - for suffix in suffixes + ppsuffixes: - SCons.Tool.SourceFileScanner.add_scanner(suffix, fscan) - - env.AppendUnique(FORTRANSUFFIXES = suffixes + ppsuffixes) - - compaction, compppaction, shcompaction, shcompppaction = \ - CreateDialectActions(dialect) - - static_obj, shared_obj = SCons.Tool.createObjBuilders(env) - - for suffix in suffixes: - static_obj.add_action(suffix, compaction) - shared_obj.add_action(suffix, shcompaction) - static_obj.add_emitter(suffix, FortranEmitter) - shared_obj.add_emitter(suffix, ShFortranEmitter) - - for suffix in ppsuffixes: - static_obj.add_action(suffix, compppaction) - shared_obj.add_action(suffix, shcompppaction) - static_obj.add_emitter(suffix, FortranEmitter) - shared_obj.add_emitter(suffix, ShFortranEmitter) - - if '%sFLAGS' % dialect not in env: - env['%sFLAGS' % dialect] = SCons.Util.CLVar('') - - if 'SH%sFLAGS' % dialect not in env: - env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS' % dialect) - - # If a tool does not define fortran prefix/suffix for include path, use C ones - if 'INC%sPREFIX' % dialect not in env: - env['INC%sPREFIX' % dialect] = '$INCPREFIX' - - if 'INC%sSUFFIX' % dialect not in env: - env['INC%sSUFFIX' % dialect] = '$INCSUFFIX' - - env['_%sINCFLAGS' % dialect] = '$( ${_concat(INC%sPREFIX, %sPATH, INC%sSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' % (dialect, dialect, dialect) - - if support_module == 1: - env['%sCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect) - env['%sPPCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect) - env['SH%sCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect) - env['SH%sPPCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect) - else: - env['%sCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect) - env['%sPPCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect) - env['SH%sCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect) - env['SH%sPPCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect) - -def add_fortran_to_env(env): - """Add Builders and construction variables for Fortran to an Environment.""" - try: - FortranSuffixes = env['FORTRANFILESUFFIXES'] - except KeyError: - FortranSuffixes = ['.f', '.for', '.ftn'] - - #print "Adding %s to fortran suffixes" % FortranSuffixes - try: - FortranPPSuffixes = env['FORTRANPPFILESUFFIXES'] - except KeyError: - FortranPPSuffixes = ['.fpp', '.FPP'] - - DialectAddToEnv(env, "FORTRAN", FortranSuffixes, - FortranPPSuffixes, support_module = 1) - - env['FORTRANMODPREFIX'] = '' # like $LIBPREFIX - env['FORTRANMODSUFFIX'] = '.mod' # like $LIBSUFFIX - - env['FORTRANMODDIR'] = '' # where the compiler should place .mod files - env['FORTRANMODDIRPREFIX'] = '' # some prefix to $FORTRANMODDIR - similar to $INCPREFIX - env['FORTRANMODDIRSUFFIX'] = '' # some suffix to $FORTRANMODDIR - similar to $INCSUFFIX - env['_FORTRANMODFLAG'] = '$( ${_concat(FORTRANMODDIRPREFIX, FORTRANMODDIR, FORTRANMODDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' - -def add_f77_to_env(env): - """Add Builders and construction variables for f77 to an Environment.""" - try: - F77Suffixes = env['F77FILESUFFIXES'] - except KeyError: - F77Suffixes = ['.f77'] - - #print "Adding %s to f77 suffixes" % F77Suffixes - try: - F77PPSuffixes = env['F77PPFILESUFFIXES'] - except KeyError: - F77PPSuffixes = [] - - DialectAddToEnv(env, "F77", F77Suffixes, F77PPSuffixes) - -def add_f90_to_env(env): - """Add Builders and construction variables for f90 to an Environment.""" - try: - F90Suffixes = env['F90FILESUFFIXES'] - except KeyError: - F90Suffixes = ['.f90'] - - #print "Adding %s to f90 suffixes" % F90Suffixes - try: - F90PPSuffixes = env['F90PPFILESUFFIXES'] - except KeyError: - F90PPSuffixes = [] - - DialectAddToEnv(env, "F90", F90Suffixes, F90PPSuffixes, - support_module = 1) - -def add_f95_to_env(env): - """Add Builders and construction variables for f95 to an Environment.""" - try: - F95Suffixes = env['F95FILESUFFIXES'] - except KeyError: - F95Suffixes = ['.f95'] - - #print "Adding %s to f95 suffixes" % F95Suffixes - try: - F95PPSuffixes = env['F95PPFILESUFFIXES'] - except KeyError: - F95PPSuffixes = [] - - DialectAddToEnv(env, "F95", F95Suffixes, F95PPSuffixes, - support_module = 1) - -def add_f03_to_env(env): - """Add Builders and construction variables for f03 to an Environment.""" - try: - F03Suffixes = env['F03FILESUFFIXES'] - except KeyError: - F03Suffixes = ['.f03'] - - #print "Adding %s to f95 suffixes" % F95Suffixes - try: - F03PPSuffixes = env['F03PPFILESUFFIXES'] - except KeyError: - F03PPSuffixes = [] - - DialectAddToEnv(env, "F03", F03Suffixes, F03PPSuffixes, - support_module = 1) - -def add_all_to_env(env): - """Add builders and construction variables for all supported fortran - dialects.""" - add_fortran_to_env(env) - add_f77_to_env(env) - add_f90_to_env(env) - add_f95_to_env(env) - add_f03_to_env(env) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/GettextCommon.py b/scons/scons-local-2.2.0/SCons/Tool/GettextCommon.py deleted file mode 100644 index b2d848c44..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/GettextCommon.py +++ /dev/null @@ -1,429 +0,0 @@ -"""SCons.Tool.GettextCommon module - -Used by several tools of `gettext` toolset. -""" - -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Tool/GettextCommon.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Warnings -import re - -############################################################################# -class XgettextToolWarning(SCons.Warnings.Warning): pass -class XgettextNotFound(XgettextToolWarning): pass -class MsginitToolWarning(SCons.Warnings.Warning): pass -class MsginitNotFound(MsginitToolWarning): pass -class MsgmergeToolWarning(SCons.Warnings.Warning): pass -class MsgmergeNotFound(MsgmergeToolWarning): pass -class MsgfmtToolWarning(SCons.Warnings.Warning): pass -class MsgfmtNotFound(MsgfmtToolWarning): pass -############################################################################# -SCons.Warnings.enableWarningClass(XgettextToolWarning) -SCons.Warnings.enableWarningClass(XgettextNotFound) -SCons.Warnings.enableWarningClass(MsginitToolWarning) -SCons.Warnings.enableWarningClass(MsginitNotFound) -SCons.Warnings.enableWarningClass(MsgmergeToolWarning) -SCons.Warnings.enableWarningClass(MsgmergeNotFound) -SCons.Warnings.enableWarningClass(MsgfmtToolWarning) -SCons.Warnings.enableWarningClass(MsgfmtNotFound) -############################################################################# - -############################################################################# -class _POTargetFactory(object): - """ A factory of `PO` target files. - - Factory defaults differ from these of `SCons.Node.FS.FS`. We set `precious` - (this is required by builders and actions gettext) and `noclean` flags by - default for all produced nodes. - """ - def __init__( self, env, nodefault = True, alias = None, precious = True - , noclean = True ): - """ Object constructor. - - **Arguments** - - - *env* (`SCons.Environment.Environment`) - - *nodefault* (`boolean`) - if `True`, produced nodes will be ignored - from default target `'.'` - - *alias* (`string`) - if provided, produced nodes will be automatically - added to this alias, and alias will be set as `AlwaysBuild` - - *precious* (`boolean`) - if `True`, the produced nodes will be set as - `Precious`. - - *noclen* (`boolean`) - if `True`, the produced nodes will be excluded - from `Clean`. - """ - self.env = env - self.alias = alias - self.precious = precious - self.noclean = noclean - self.nodefault = nodefault - - def _create_node(self, name, factory, directory = None, create = 1): - """ Create node, and set it up to factory settings. """ - import SCons.Util - node = factory(name, directory, create) - node.set_noclean(self.noclean) - node.set_precious(self.precious) - if self.nodefault: - self.env.Ignore('.', node) - if self.alias: - self.env.AlwaysBuild(self.env.Alias(self.alias, node)) - return node - - def Entry(self, name, directory = None, create = 1): - """ Create `SCons.Node.FS.Entry` """ - return self._create_node(name, self.env.fs.Entry, directory, create) - - def File(self, name, directory = None, create = 1): - """ Create `SCons.Node.FS.File` """ - return self._create_node(name, self.env.fs.File, directory, create) -############################################################################# - -############################################################################# -_re_comment = re.compile(r'(#[^\n\r]+)$', re.M) -_re_lang = re.compile(r'([a-zA-Z0-9_]+)', re.M) -############################################################################# -def _read_linguas_from_files(env, linguas_files = None): - """ Parse `LINGUAS` file and return list of extracted languages """ - import SCons.Util - import SCons.Environment - global _re_comment - global _re_lang - if not SCons.Util.is_List(linguas_files) \ - and not SCons.Util.is_String(linguas_files) \ - and not isinstance(linguas_files, SCons.Node.FS.Base) \ - and linguas_files: - # If, linguas_files==True or such, then read 'LINGUAS' file. - linguas_files = [ 'LINGUAS' ] - if linguas_files is None: - return [] - fnodes = env.arg2nodes(linguas_files) - linguas = [] - for fnode in fnodes: - contents = _re_comment.sub("", fnode.get_text_contents()) - ls = [ l for l in _re_lang.findall(contents) if l ] - linguas.extend(ls) - return linguas -############################################################################# - -############################################################################# -from SCons.Builder import BuilderBase -############################################################################# -class _POFileBuilder(BuilderBase): - """ `PO` file builder. - - This is multi-target single-source builder. In typical situation the source - is single `POT` file, e.g. `messages.pot`, and there are multiple `PO` - targets to be updated from this `POT`. We must run - `SCons.Builder.BuilderBase._execute()` separatelly for each target to track - dependencies separatelly for each target file. - - **NOTE**: if we call `SCons.Builder.BuilderBase._execute(.., target, ...)` - with target being list of all targets, all targets would be rebuilt each time - one of the targets from this list is missing. This would happen, for example, - when new language `ll` enters `LINGUAS_FILE` (at this moment there is no - `ll.po` file yet). To avoid this, we override - `SCons.Builder.BuilerBase._execute()` and call it separatelly for each - target. Here we also append to the target list the languages read from - `LINGUAS_FILE`. - """ - # - #* The argument for overriding _execute(): We must use environment with - # builder overrides applied (see BuilderBase.__init__(). Here it comes for - # free. - #* The argument against using 'emitter': The emitter is called too late - # by BuilderBase._execute(). If user calls, for example: - # - # env.POUpdate(LINGUAS_FILE = 'LINGUAS') - # - # the builder throws error, because it is called with target=None, - # source=None and is trying to "generate" sources or target list first. - # If user calls - # - # env.POUpdate(['foo', 'baz'], LINGUAS_FILE = 'LINGUAS') - # - # the env.BuilderWrapper() calls our builder with target=None, - # source=['foo', 'baz']. The BuilderBase._execute() then splits execution - # and execute iterativelly (recursion) self._execute(None, source[i]). - # After that it calls emitter (which is quite too late). The emitter is - # also called in each iteration, what makes things yet worse. - def __init__(self, env, **kw): - if not 'suffix' in kw: - kw['suffix'] = '$POSUFFIX' - if not 'src_suffix' in kw: - kw['src_suffix'] = '$POTSUFFIX' - if not 'src_builder' in kw: - kw['src_builder'] = '_POTUpdateBuilder' - if not 'single_source' in kw: - kw['single_source'] = True - alias = None - if 'target_alias' in kw: - alias = kw['target_alias'] - del kw['target_alias'] - if not 'target_factory' in kw: - kw['target_factory'] = _POTargetFactory(env, alias=alias).File - BuilderBase.__init__(self, **kw) - - def _execute(self, env, target, source, *args, **kw): - """ Execute builder's actions. - - Here we append to `target` the languages read from `$LINGUAS_FILE` and - apply `SCons.Builder.BuilderBase._execute()` separatelly to each target. - The arguments and return value are same as for - `SCons.Builder.BuilderBase._execute()`. - """ - import SCons.Util - import SCons.Node - linguas_files = None - if env.has_key('LINGUAS_FILE') and env['LINGUAS_FILE']: - linguas_files = env['LINGUAS_FILE'] - # This prevents endless recursion loop (we'll be invoked once for - # each target appended here, we must not extend the list again). - env['LINGUAS_FILE'] = None - linguas = _read_linguas_from_files(env,linguas_files) - if SCons.Util.is_List(target): - target.extend(linguas) - elif target is not None: - target = [target] + linguas - else: - target = linguas - if not target: - # Let the SCons.BuilderBase to handle this patologic situation - return BuilderBase._execute( self, env, target, source, *args, **kw) - # The rest is ours - if not SCons.Util.is_List(target): - target = [ target ] - result = [] - for tgt in target: - r = BuilderBase._execute( self, env, [tgt], source, *args, **kw) - result.extend(r) - if linguas_files is not None: - env['LINGUAS_FILE'] = linguas_files - return SCons.Node.NodeList(result) -############################################################################# - -import SCons.Environment -############################################################################# -def _translate(env, target=[], source=SCons.Environment._null, *args, **kw): - """ Function for `Translate()` pseudo-builder """ - pot = env.POTUpdate(None, source, *args, **kw) - po = env.POUpdate(target, pot, *args, **kw) - return po -############################################################################# - -############################################################################# -class RPaths(object): - """ Callable object, which returns pathnames relative to SCons current - working directory. - - It seems like `SCons.Node.FS.Base.get_path()` returns absolute paths - for nodes that are outside of current working directory (`env.fs.getcwd()`). - Here, we often have `SConscript`, `POT` and `PO` files within `po/` - directory and source files (e.g. `*.c`) outside of it. When generating `POT` - template file, references to source files are written to `POT` template, so - a translator may later quickly jump to appropriate source file and line from - its `PO` editor (e.g. `poedit`). Relative paths in `PO` file are usually - interpreted by `PO` editor as paths relative to the place, where `PO` file - lives. The absolute paths would make resultant `POT` file nonportable, as - the references would be correct only on the machine, where `POT` file was - recently re-created. For such reason, we need a function, which always - returns relative paths. This is the purpose of `RPaths` callable object. - - The `__call__` method returns paths relative to current woking directory, but - we assume, that *xgettext(1)* is run from the directory, where target file is - going to be created. - - Note, that this may not work for files distributed over several hosts or - across different drives on windows. We assume here, that single local - filesystem holds both source files and target `POT` templates. - - Intended use of `RPaths` - in `xgettext.py`:: - - def generate(env): - from GettextCommon import RPaths - ... - sources = '$( ${_concat( "", SOURCES, "", __env__, XgettextRPaths, TARGET, SOURCES)} $)' - env.Append( - ... - XGETTEXTCOM = 'XGETTEXT ... ' + sources, - ... - XgettextRPaths = RPaths(env) - ) - """ - # NOTE: This callable object returns pathnames of dirs/files relative to - # current working directory. The pathname remains relative also for entries - # that are outside of current working directory (node, that - # SCons.Node.FS.File and siblings return absolute path in such case). For - # simplicity we compute path relative to current working directory, this - # seems be enough for our purposes (don't need TARGET variable and - # SCons.Defaults.Variable_Caller stuff). - - def __init__(self, env): - """ Initialize `RPaths` callable object. - - **Arguments**: - - - *env* - a `SCons.Environment.Environment` object, defines *current - working dir*. - """ - self.env = env - - # FIXME: I'm not sure, how it should be implemented (what the *args are in - # general, what is **kw). - def __call__(self, nodes, *args, **kw): - """ Return nodes' paths (strings) relative to current working directory. - - **Arguments**: - - - *nodes* ([`SCons.Node.FS.Base`]) - list of nodes. - - *args* - currently unused. - - *kw* - currently unused. - - **Returns**: - - - Tuple of strings, which represent paths relative to current working - directory (for given environment). - """ - # os.path.relpath is available only on python >= 2.6. We use our own - # implementation. It's taken from BareNecessities package: - # http://jimmyg.org/work/code/barenecessities/index.html - from posixpath import curdir - def relpath(path, start=curdir): - import posixpath - """Return a relative version of a path""" - if not path: - raise ValueError("no path specified") - start_list = posixpath.abspath(start).split(posixpath.sep) - path_list = posixpath.abspath(path).split(posixpath.sep) - # Work out how much of the filepath is shared by start and path. - i = len(posixpath.commonprefix([start_list, path_list])) - rel_list = [posixpath.pardir] * (len(start_list)-i) + path_list[i:] - if not rel_list: - return posixpath.curdir - return posixpath.join(*rel_list) - import os - import SCons.Node.FS - rpaths = () - cwd = self.env.fs.getcwd().get_abspath() - for node in nodes: - rpath = None - if isinstance(node, SCons.Node.FS.Base): - rpath = relpath(node.get_abspath(), cwd) - # FIXME: Other types possible here? - if rpath is not None: - rpaths += (rpath,) - return rpaths -############################################################################# - -############################################################################# -def _init_po_files(target, source, env): - """ Action function for `POInit` builder. """ - nop = lambda target, source, env : 0 - if env.has_key('POAUTOINIT'): - autoinit = env['POAUTOINIT'] - else: - autoinit = False - # Well, if everything outside works well, this loop should do single - # iteration. Otherwise we are rebuilding all the targets even, if just - # one has changed (but is this out fault?). - for tgt in target: - if not tgt.exists(): - if autoinit: - action = SCons.Action.Action('$MSGINITCOM', '$MSGINITCOMSTR') - else: - msg = 'File ' + repr(str(tgt)) + ' does not exist. ' \ - + 'If you are a translator, you can create it through: \n' \ - + '$MSGINITCOM' - action = SCons.Action.Action(nop, msg) - status = action([tgt], source, env) - if status: return status - return 0 -############################################################################# - -############################################################################# -def _detect_xgettext(env): - """ Detects *xgettext(1)* binary """ - if env.has_key('XGETTEXT'): - return env['XGETTEXT'] - xgettext = env.Detect('xgettext'); - if xgettext: - return xgettext - raise SCons.Errors.StopError(XgettextNotFound,"Could not detect xgettext") - return None -############################################################################# -def _xgettext_exists(env): - return _detect_xgettext(env) -############################################################################# - -############################################################################# -def _detect_msginit(env): - """ Detects *msginit(1)* program. """ - if env.has_key('MSGINIT'): - return env['MSGINIT'] - msginit = env.Detect('msginit'); - if msginit: - return msginit - raise SCons.Errors.StopError(MsginitNotFound, "Could not detect msginit") - return None -############################################################################# -def _msginit_exists(env): - return _detect_msginit(env) -############################################################################# - -############################################################################# -def _detect_msgmerge(env): - """ Detects *msgmerge(1)* program. """ - if env.has_key('MSGMERGE'): - return env['MSGMERGE'] - msgmerge = env.Detect('msgmerge'); - if msgmerge: - return msgmerge - raise SCons.Errors.StopError(MsgmergeNotFound, "Could not detect msgmerge") - return None -############################################################################# -def _msgmerge_exists(env): - return _detect_msgmerge(env) -############################################################################# - -############################################################################# -def _detect_msgfmt(env): - """ Detects *msgmfmt(1)* program. """ - if env.has_key('MSGFMT'): - return env['MSGFMT'] - msgfmt = env.Detect('msgfmt'); - if msgfmt: - return msgfmt - raise SCons.Errors.StopError(MsgfmtNotFound, "Could not detect msgfmt") - return None -############################################################################# -def _msgfmt_exists(env): - return _detect_msgfmt(env) -############################################################################# - -############################################################################# -def tool_list(platform, env): - """ List tools that shall be generated by top-level `gettext` tool """ - return [ 'xgettext', 'msginit', 'msgmerge', 'msgfmt' ] -############################################################################# - diff --git a/scons/scons-local-2.2.0/SCons/Tool/JavaCommon.py b/scons/scons-local-2.2.0/SCons/Tool/JavaCommon.py deleted file mode 100644 index dc381053e..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/JavaCommon.py +++ /dev/null @@ -1,323 +0,0 @@ -"""SCons.Tool.JavaCommon - -Stuff for processing Java. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/JavaCommon.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os -import os.path -import re - -java_parsing = 1 - -default_java_version = '1.4' - -if java_parsing: - # Parse Java files for class names. - # - # This is a really cool parser from Charles Crain - # that finds appropriate class names in Java source. - - # A regular expression that will find, in a java file: - # newlines; - # double-backslashes; - # a single-line comment "//"; - # single or double quotes preceeded by a backslash; - # single quotes, double quotes, open or close braces, semi-colons, - # periods, open or close parentheses; - # floating-point numbers; - # any alphanumeric token (keyword, class name, specifier); - # any alphanumeric token surrounded by angle brackets (generics); - # the multi-line comment begin and end tokens /* and */; - # array declarations "[]". - _reToken = re.compile(r'(\n|\\\\|//|\\[\'"]|[\'"\{\}\;\.\(\)]|' + - r'\d*\.\d*|[A-Za-z_][\w\$\.]*|<[A-Za-z_]\w+>|' + - r'/\*|\*/|\[\])') - - class OuterState(object): - """The initial state for parsing a Java file for classes, - interfaces, and anonymous inner classes.""" - def __init__(self, version=default_java_version): - - if not version in ('1.1', '1.2', '1.3','1.4', '1.5', '1.6', - '5', '6'): - msg = "Java version %s not supported" % version - raise NotImplementedError(msg) - - self.version = version - self.listClasses = [] - self.listOutputs = [] - self.stackBrackets = [] - self.brackets = 0 - self.nextAnon = 1 - self.localClasses = [] - self.stackAnonClassBrackets = [] - self.anonStacksStack = [[0]] - self.package = None - - def trace(self): - pass - - def __getClassState(self): - try: - return self.classState - except AttributeError: - ret = ClassState(self) - self.classState = ret - return ret - - def __getPackageState(self): - try: - return self.packageState - except AttributeError: - ret = PackageState(self) - self.packageState = ret - return ret - - def __getAnonClassState(self): - try: - return self.anonState - except AttributeError: - self.outer_state = self - ret = SkipState(1, AnonClassState(self)) - self.anonState = ret - return ret - - def __getSkipState(self): - try: - return self.skipState - except AttributeError: - ret = SkipState(1, self) - self.skipState = ret - return ret - - def __getAnonStack(self): - return self.anonStacksStack[-1] - - def openBracket(self): - self.brackets = self.brackets + 1 - - def closeBracket(self): - self.brackets = self.brackets - 1 - if len(self.stackBrackets) and \ - self.brackets == self.stackBrackets[-1]: - self.listOutputs.append('$'.join(self.listClasses)) - self.localClasses.pop() - self.listClasses.pop() - self.anonStacksStack.pop() - self.stackBrackets.pop() - if len(self.stackAnonClassBrackets) and \ - self.brackets == self.stackAnonClassBrackets[-1]: - self.__getAnonStack().pop() - self.stackAnonClassBrackets.pop() - - def parseToken(self, token): - if token[:2] == '//': - return IgnoreState('\n', self) - elif token == '/*': - return IgnoreState('*/', self) - elif token == '{': - self.openBracket() - elif token == '}': - self.closeBracket() - elif token in [ '"', "'" ]: - return IgnoreState(token, self) - elif token == "new": - # anonymous inner class - if len(self.listClasses) > 0: - return self.__getAnonClassState() - return self.__getSkipState() # Skip the class name - elif token in ['class', 'interface', 'enum']: - if len(self.listClasses) == 0: - self.nextAnon = 1 - self.stackBrackets.append(self.brackets) - return self.__getClassState() - elif token == 'package': - return self.__getPackageState() - elif token == '.': - # Skip the attribute, it might be named "class", in which - # case we don't want to treat the following token as - # an inner class name... - return self.__getSkipState() - return self - - def addAnonClass(self): - """Add an anonymous inner class""" - if self.version in ('1.1', '1.2', '1.3', '1.4'): - clazz = self.listClasses[0] - self.listOutputs.append('%s$%d' % (clazz, self.nextAnon)) - elif self.version in ('1.5', '1.6', '5', '6'): - self.stackAnonClassBrackets.append(self.brackets) - className = [] - className.extend(self.listClasses) - self.__getAnonStack()[-1] = self.__getAnonStack()[-1] + 1 - for anon in self.__getAnonStack(): - className.append(str(anon)) - self.listOutputs.append('$'.join(className)) - - self.nextAnon = self.nextAnon + 1 - self.__getAnonStack().append(0) - - def setPackage(self, package): - self.package = package - - class AnonClassState(object): - """A state that looks for anonymous inner classes.""" - def __init__(self, old_state): - # outer_state is always an instance of OuterState - self.outer_state = old_state.outer_state - self.old_state = old_state - self.brace_level = 0 - def parseToken(self, token): - # This is an anonymous class if and only if the next - # non-whitespace token is a bracket. Everything between - # braces should be parsed as normal java code. - if token[:2] == '//': - return IgnoreState('\n', self) - elif token == '/*': - return IgnoreState('*/', self) - elif token == '\n': - return self - elif token[0] == '<' and token[-1] == '>': - return self - elif token == '(': - self.brace_level = self.brace_level + 1 - return self - if self.brace_level > 0: - if token == 'new': - # look further for anonymous inner class - return SkipState(1, AnonClassState(self)) - elif token in [ '"', "'" ]: - return IgnoreState(token, self) - elif token == ')': - self.brace_level = self.brace_level - 1 - return self - if token == '{': - self.outer_state.addAnonClass() - return self.old_state.parseToken(token) - - class SkipState(object): - """A state that will skip a specified number of tokens before - reverting to the previous state.""" - def __init__(self, tokens_to_skip, old_state): - self.tokens_to_skip = tokens_to_skip - self.old_state = old_state - def parseToken(self, token): - self.tokens_to_skip = self.tokens_to_skip - 1 - if self.tokens_to_skip < 1: - return self.old_state - return self - - class ClassState(object): - """A state we go into when we hit a class or interface keyword.""" - def __init__(self, outer_state): - # outer_state is always an instance of OuterState - self.outer_state = outer_state - def parseToken(self, token): - # the next non-whitespace token should be the name of the class - if token == '\n': - return self - # If that's an inner class which is declared in a method, it - # requires an index prepended to the class-name, e.g. - # 'Foo$1Inner' (Tigris Issue 2087) - if self.outer_state.localClasses and \ - self.outer_state.stackBrackets[-1] > \ - self.outer_state.stackBrackets[-2]+1: - locals = self.outer_state.localClasses[-1] - try: - idx = locals[token] - locals[token] = locals[token]+1 - except KeyError: - locals[token] = 1 - token = str(locals[token]) + token - self.outer_state.localClasses.append({}) - self.outer_state.listClasses.append(token) - self.outer_state.anonStacksStack.append([0]) - return self.outer_state - - class IgnoreState(object): - """A state that will ignore all tokens until it gets to a - specified token.""" - def __init__(self, ignore_until, old_state): - self.ignore_until = ignore_until - self.old_state = old_state - def parseToken(self, token): - if self.ignore_until == token: - return self.old_state - return self - - class PackageState(object): - """The state we enter when we encounter the package keyword. - We assume the next token will be the package name.""" - def __init__(self, outer_state): - # outer_state is always an instance of OuterState - self.outer_state = outer_state - def parseToken(self, token): - self.outer_state.setPackage(token) - return self.outer_state - - def parse_java_file(fn, version=default_java_version): - return parse_java(open(fn, 'r').read(), version) - - def parse_java(contents, version=default_java_version, trace=None): - """Parse a .java file and return a double of package directory, - plus a list of .class files that compiling that .java file will - produce""" - package = None - initial = OuterState(version) - currstate = initial - for token in _reToken.findall(contents): - # The regex produces a bunch of groups, but only one will - # have anything in it. - currstate = currstate.parseToken(token) - if trace: trace(token, currstate) - if initial.package: - package = initial.package.replace('.', os.sep) - return (package, initial.listOutputs) - -else: - # Don't actually parse Java files for class names. - # - # We might make this a configurable option in the future if - # Java-file parsing takes too long (although it shouldn't relative - # to how long the Java compiler itself seems to take...). - - def parse_java_file(fn): - """ "Parse" a .java file. - - This actually just splits the file name, so the assumption here - is that the file name matches the public class name, and that - the path to the file is the same as the package name. - """ - return os.path.split(file) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/MSCommon/__init__.py b/scons/scons-local-2.2.0/SCons/Tool/MSCommon/__init__.py deleted file mode 100644 index 8dc6c5a61..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/MSCommon/__init__.py +++ /dev/null @@ -1,56 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/MSCommon/__init__.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -__doc__ = """ -Common functions for Microsoft Visual Studio and Visual C/C++. -""" - -import copy -import os -import re -import subprocess - -import SCons.Errors -import SCons.Platform.win32 -import SCons.Util - -from SCons.Tool.MSCommon.sdk import mssdk_exists, \ - mssdk_setup_env - -from SCons.Tool.MSCommon.vc import msvc_exists, \ - msvc_setup_env, \ - msvc_setup_env_once - -from SCons.Tool.MSCommon.vs import get_default_version, \ - get_vs_by_version, \ - merge_default_version, \ - msvs_exists, \ - query_versions - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/MSCommon/arch.py b/scons/scons-local-2.2.0/SCons/Tool/MSCommon/arch.py deleted file mode 100644 index 1b6ac9ef0..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/MSCommon/arch.py +++ /dev/null @@ -1,61 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/MSCommon/arch.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -__doc__ = """Module to define supported Windows chip architectures. -""" - -import os - -class ArchDefinition(object): - """ - A class for defining architecture-specific settings and logic. - """ - def __init__(self, arch, synonyms=[]): - self.arch = arch - self.synonyms = synonyms - -SupportedArchitectureList = [ - ArchitectureDefinition( - 'x86', - ['i386', 'i486', 'i586', 'i686'], - ), - - ArchitectureDefinition( - 'x86_64', - ['AMD64', 'amd64', 'em64t', 'EM64T', 'x86_64'], - ), - - ArchitectureDefinition( - 'ia64', - ['IA64'], - ), -] - -SupportedArchitectureMap = {} -for a in SupportedArchitectureList: - SupportedArchitectureMap[a.arch] = a - for s in a.synonyms: - SupportedArchitectureMap[s] = a - diff --git a/scons/scons-local-2.2.0/SCons/Tool/MSCommon/common.py b/scons/scons-local-2.2.0/SCons/Tool/MSCommon/common.py deleted file mode 100644 index d10b7636c..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/MSCommon/common.py +++ /dev/null @@ -1,240 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/MSCommon/common.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -__doc__ = """ -Common helper functions for working with the Microsoft tool chain. -""" - -import copy -import os -import subprocess -import re - -import SCons.Util - - -logfile = os.environ.get('SCONS_MSCOMMON_DEBUG') -if logfile == '-': - def debug(x): - print x -elif logfile: - try: - import logging - except ImportError: - debug = lambda x: open(logfile, 'a').write(x + '\n') - else: - logging.basicConfig(filename=logfile, level=logging.DEBUG) - debug = logging.debug -else: - debug = lambda x: None - - -_is_win64 = None - -def is_win64(): - """Return true if running on windows 64 bits. - - Works whether python itself runs in 64 bits or 32 bits.""" - # Unfortunately, python does not provide a useful way to determine - # if the underlying Windows OS is 32-bit or 64-bit. Worse, whether - # the Python itself is 32-bit or 64-bit affects what it returns, - # so nothing in sys.* or os.* help. - - # Apparently the best solution is to use env vars that Windows - # sets. If PROCESSOR_ARCHITECTURE is not x86, then the python - # process is running in 64 bit mode (on a 64-bit OS, 64-bit - # hardware, obviously). - # If this python is 32-bit but the OS is 64, Windows will set - # ProgramW6432 and PROCESSOR_ARCHITEW6432 to non-null. - # (Checking for HKLM\Software\Wow6432Node in the registry doesn't - # work, because some 32-bit installers create it.) - global _is_win64 - if _is_win64 is None: - # I structured these tests to make it easy to add new ones or - # add exceptions in the future, because this is a bit fragile. - _is_win64 = False - if os.environ.get('PROCESSOR_ARCHITECTURE','x86') != 'x86': - _is_win64 = True - if os.environ.get('PROCESSOR_ARCHITEW6432'): - _is_win64 = True - if os.environ.get('ProgramW6432'): - _is_win64 = True - return _is_win64 - - -def read_reg(value): - return SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, value)[0] - -def has_reg(value): - """Return True if the given key exists in HKEY_LOCAL_MACHINE, False - otherwise.""" - try: - SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, value) - ret = True - except WindowsError: - ret = False - return ret - -# Functions for fetching environment variable settings from batch files. - -def normalize_env(env, keys, force=False): - """Given a dictionary representing a shell environment, add the variables - from os.environ needed for the processing of .bat files; the keys are - controlled by the keys argument. - - It also makes sure the environment values are correctly encoded. - - If force=True, then all of the key values that exist are copied - into the returned dictionary. If force=false, values are only - copied if the key does not already exist in the copied dictionary. - - Note: the environment is copied.""" - normenv = {} - if env: - for k in env.keys(): - normenv[k] = copy.deepcopy(env[k]).encode('mbcs') - - for k in keys: - if k in os.environ and (force or not k in normenv): - normenv[k] = os.environ[k].encode('mbcs') - - return normenv - -def get_output(vcbat, args = None, env = None): - """Parse the output of given bat file, with given args.""" - - if env is None: - # Create a blank environment, for use in launching the tools - env = SCons.Environment.Environment(tools=[]) - - # TODO: This is a hard-coded list of the variables that (may) need - # to be imported from os.environ[] for v[sc]*vars*.bat file - # execution to work. This list should really be either directly - # controlled by vc.py, or else derived from the common_tools_var - # settings in vs.py. - vars = [ - 'COMSPEC', - 'VS90COMNTOOLS', - 'VS80COMNTOOLS', - 'VS71COMNTOOLS', - 'VS70COMNTOOLS', - 'VS60COMNTOOLS', - ] - env['ENV'] = normalize_env(env['ENV'], vars, force=False) - - if args: - debug("Calling '%s %s'" % (vcbat, args)) - popen = SCons.Action._subproc(env, - '"%s" %s & set' % (vcbat, args), - stdin = 'devnull', - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - else: - debug("Calling '%s'" % vcbat) - popen = SCons.Action._subproc(env, - '"%s" & set' % vcbat, - stdin = 'devnull', - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - - # Use the .stdout and .stderr attributes directly because the - # .communicate() method uses the threading module on Windows - # and won't work under Pythons not built with threading. - stdout = popen.stdout.read() - stderr = popen.stderr.read() - if stderr: - # TODO: find something better to do with stderr; - # this at least prevents errors from getting swallowed. - import sys - sys.stderr.write(stderr) - if popen.wait() != 0: - raise IOError(stderr.decode("mbcs")) - - output = stdout.decode("mbcs") - return output - -def parse_output(output, keep = ("INCLUDE", "LIB", "LIBPATH", "PATH")): - # dkeep is a dict associating key: path_list, where key is one item from - # keep, and pat_list the associated list of paths - - dkeep = dict([(i, []) for i in keep]) - - # rdk will keep the regex to match the .bat file output line starts - rdk = {} - for i in keep: - rdk[i] = re.compile('%s=(.*)' % i, re.I) - - def add_env(rmatch, key, dkeep=dkeep): - plist = rmatch.group(1).split(os.pathsep) - for p in plist: - # Do not add empty paths (when a var ends with ;) - if p: - p = p.encode('mbcs') - # XXX: For some reason, VC98 .bat file adds "" around the PATH - # values, and it screws up the environment later, so we strip - # it. - p = p.strip('"') - dkeep[key].append(p) - - for line in output.splitlines(): - for k,v in rdk.items(): - m = v.match(line) - if m: - add_env(m, k) - - return dkeep - -# TODO(sgk): unused -def output_to_dict(output): - """Given an output string, parse it to find env variables. - - Return a dict where keys are variables names, and values their content""" - envlinem = re.compile(r'^([a-zA-z0-9]+)=([\S\s]*)$') - parsedenv = {} - for line in output.splitlines(): - m = envlinem.match(line) - if m: - parsedenv[m.group(1)] = m.group(2) - return parsedenv - -# TODO(sgk): unused -def get_new(l1, l2): - """Given two list l1 and l2, return the items in l2 which are not in l1. - Order is maintained.""" - - # We don't try to be smart: lists are small, and this is not the bottleneck - # is any case - new = [] - for i in l2: - if i not in l1: - new.append(i) - - return new - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/MSCommon/netframework.py b/scons/scons-local-2.2.0/SCons/Tool/MSCommon/netframework.py deleted file mode 100644 index cc5aaf1bf..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/MSCommon/netframework.py +++ /dev/null @@ -1,82 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Tool/MSCommon/netframework.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -__doc__ = """ -""" - -import os -import re - -from common import read_reg, debug - -# Original value recorded by dcournapeau -_FRAMEWORKDIR_HKEY_ROOT = r'Software\Microsoft\.NETFramework\InstallRoot' -# On SGK's system -_FRAMEWORKDIR_HKEY_ROOT = r'Software\Microsoft\Microsoft SDKs\.NETFramework\v2.0\InstallationFolder' - -def find_framework_root(): - # XXX: find it from environment (FrameworkDir) - try: - froot = read_reg(_FRAMEWORKDIR_HKEY_ROOT) - debug("Found framework install root in registry: %s" % froot) - except WindowsError, e: - debug("Could not read reg key %s" % _FRAMEWORKDIR_HKEY_ROOT) - return None - - if not os.path.exists(froot): - debug("%s not found on fs" % froot) - return None - - return froot - -def query_versions(): - froot = find_framework_root() - if froot: - contents = os.listdir(froot) - - l = re.compile('v[0-9]+.*') - versions = [e for e in contents if l.match(e)] - - def versrt(a,b): - # since version numbers aren't really floats... - aa = a[1:] - bb = b[1:] - aal = aa.split('.') - bbl = bb.split('.') - # sequence comparison in python is lexicographical - # which is exactly what we want. - # Note we sort backwards so the highest version is first. - return cmp(bbl,aal) - - versions.sort(versrt) - else: - versions = [] - - return versions - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/MSCommon/sdk.py b/scons/scons-local-2.2.0/SCons/Tool/MSCommon/sdk.py deleted file mode 100644 index fd22cd7be..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/MSCommon/sdk.py +++ /dev/null @@ -1,391 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/MSCommon/sdk.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -__doc__ = """Module to detect the Platform/Windows SDK - -PSDK 2003 R1 is the earliest version detected. -""" - -import os - -import SCons.Errors -import SCons.Util - -import common - -debug = common.debug - -# SDK Checks. This is of course a mess as everything else on MS platforms. Here -# is what we do to detect the SDK: -# -# For Windows SDK >= 6.0: just look into the registry entries: -# HKLM\Software\Microsoft\Microsoft SDKs\Windows -# All the keys in there are the available versions. -# -# For Platform SDK before 6.0 (2003 server R1 and R2, etc...), there does not -# seem to be any sane registry key, so the precise location is hardcoded. -# -# For versions below 2003R1, it seems the PSDK is included with Visual Studio? -# -# Also, per the following: -# http://benjamin.smedbergs.us/blog/tag/atl/ -# VC++ Professional comes with the SDK, VC++ Express does not. - -# Location of the SDK (checked for 6.1 only) -_CURINSTALLED_SDK_HKEY_ROOT = \ - r"Software\Microsoft\Microsoft SDKs\Windows\CurrentInstallFolder" - - -class SDKDefinition(object): - """ - An abstract base class for trying to find installed SDK directories. - """ - def __init__(self, version, **kw): - self.version = version - self.__dict__.update(kw) - - def find_sdk_dir(self): - """Try to find the MS SDK from the registry. - - Return None if failed or the directory does not exist. - """ - if not SCons.Util.can_read_reg: - debug('find_sdk_dir(): can not read registry') - return None - - hkey = self.HKEY_FMT % self.hkey_data - debug('find_sdk_dir(): checking registry:%s'%hkey) - - try: - sdk_dir = common.read_reg(hkey) - except WindowsError, e: - debug('find_sdk_dir(): no SDK registry key %s' % repr(hkey)) - return None - - debug('find_sdk_dir(): Trying SDK Dir: %s'%sdk_dir) - - if not os.path.exists(sdk_dir): - debug('find_sdk_dir(): %s not on file system' % sdk_dir) - return None - - ftc = os.path.join(sdk_dir, self.sanity_check_file) - if not os.path.exists(ftc): - debug("find_sdk_dir(): sanity check %s not found" % ftc) - return None - - return sdk_dir - - def get_sdk_dir(self): - """Return the MSSSDK given the version string.""" - try: - return self._sdk_dir - except AttributeError: - sdk_dir = self.find_sdk_dir() - self._sdk_dir = sdk_dir - return sdk_dir - - def get_sdk_vc_script(self,host_arch, target_arch): - """ Return the script to initialize the VC compiler installed by SDK - """ - - if (host_arch == 'amd64' and target_arch == 'x86'): - # No cross tools needed compiling 32 bits on 64 bit machine - host_arch=target_arch - - arch_string=target_arch - if (host_arch != target_arch): - arch_string='%s_%s'%(host_arch,target_arch) - - debug("sdk.py: get_sdk_vc_script():arch_string:%s host_arch:%s target_arch:%s"%(arch_string, - host_arch, - target_arch)) - file=self.vc_setup_scripts.get(arch_string,None) - debug("sdk.py: get_sdk_vc_script():file:%s"%file) - return file - -class WindowsSDK(SDKDefinition): - """ - A subclass for trying to find installed Windows SDK directories. - """ - HKEY_FMT = r'Software\Microsoft\Microsoft SDKs\Windows\v%s\InstallationFolder' - def __init__(self, *args, **kw): - SDKDefinition.__init__(self, *args, **kw) - self.hkey_data = self.version - -class PlatformSDK(SDKDefinition): - """ - A subclass for trying to find installed Platform SDK directories. - """ - HKEY_FMT = r'Software\Microsoft\MicrosoftSDK\InstalledSDKS\%s\Install Dir' - def __init__(self, *args, **kw): - SDKDefinition.__init__(self, *args, **kw) - self.hkey_data = self.uuid - -# -# The list of VC initialization scripts installed by the SDK -# These should be tried if the vcvarsall.bat TARGET_ARCH fails -preSDK61VCSetupScripts = { 'x86' : r'bin\vcvars32.bat', - 'amd64' : r'bin\vcvarsamd64.bat', - 'x86_amd64': r'bin\vcvarsx86_amd64.bat', - 'x86_ia64' : r'bin\vcvarsx86_ia64.bat', - 'ia64' : r'bin\vcvarsia64.bat'} - -SDK61VCSetupScripts = {'x86' : r'bin\vcvars32.bat', - 'amd64' : r'bin\amd64\vcvarsamd64.bat', - 'x86_amd64': r'bin\x86_amd64\vcvarsx86_amd64.bat', - 'x86_ia64' : r'bin\x86_ia64\vcvarsx86_ia64.bat', - 'ia64' : r'bin\ia64\vcvarsia64.bat'} - -SDK70VCSetupScripts = { 'x86' : r'bin\vcvars32.bat', - 'amd64' : r'bin\vcvars64.bat', - 'x86_amd64': r'bin\vcvarsx86_amd64.bat', - 'x86_ia64' : r'bin\vcvarsx86_ia64.bat', - 'ia64' : r'bin\vcvarsia64.bat'} - -# The list of support SDKs which we know how to detect. -# -# The first SDK found in the list is the one used by default if there -# are multiple SDKs installed. Barring good reasons to the contrary, -# this means we should list SDKs with from most recent to oldest. -# -# If you update this list, update the documentation in Tool/mssdk.xml. -SupportedSDKList = [ - WindowsSDK('7.0', - sanity_check_file=r'bin\SetEnv.Cmd', - include_subdir='include', - lib_subdir={ - 'x86' : ['lib'], - 'x86_64' : [r'lib\x64'], - 'ia64' : [r'lib\ia64'], - }, - vc_setup_scripts = SDK70VCSetupScripts, - ), - WindowsSDK('6.1', - sanity_check_file=r'bin\SetEnv.Cmd', - include_subdir='include', - lib_subdir={ - 'x86' : ['lib'], - 'x86_64' : [r'lib\x64'], - 'ia64' : [r'lib\ia64'], - }, - vc_setup_scripts = SDK61VCSetupScripts, - ), - - WindowsSDK('6.0A', - sanity_check_file=r'include\windows.h', - include_subdir='include', - lib_subdir={ - 'x86' : ['lib'], - 'x86_64' : [r'lib\x64'], - 'ia64' : [r'lib\ia64'], - }, - vc_setup_scripts = preSDK61VCSetupScripts, - ), - - WindowsSDK('6.0', - sanity_check_file=r'bin\gacutil.exe', - include_subdir='include', - lib_subdir='lib', - vc_setup_scripts = preSDK61VCSetupScripts, - ), - - PlatformSDK('2003R2', - sanity_check_file=r'SetEnv.Cmd', - uuid="D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1", - vc_setup_scripts = preSDK61VCSetupScripts, - ), - - PlatformSDK('2003R1', - sanity_check_file=r'SetEnv.Cmd', - uuid="8F9E5EF3-A9A5-491B-A889-C58EFFECE8B3", - vc_setup_scripts = preSDK61VCSetupScripts, - ), -] - -SupportedSDKMap = {} -for sdk in SupportedSDKList: - SupportedSDKMap[sdk.version] = sdk - - -# Finding installed SDKs isn't cheap, because it goes not only to the -# registry but also to the disk to sanity-check that there is, in fact, -# an SDK installed there and that the registry entry isn't just stale. -# Find this information once, when requested, and cache it. - -InstalledSDKList = None -InstalledSDKMap = None - -def get_installed_sdks(): - global InstalledSDKList - global InstalledSDKMap - debug('sdk.py:get_installed_sdks()') - if InstalledSDKList is None: - InstalledSDKList = [] - InstalledSDKMap = {} - for sdk in SupportedSDKList: - debug('MSCommon/sdk.py: trying to find SDK %s' % sdk.version) - if sdk.get_sdk_dir(): - debug('MSCommon/sdk.py:found SDK %s' % sdk.version) - InstalledSDKList.append(sdk) - InstalledSDKMap[sdk.version] = sdk - return InstalledSDKList - - -# We may be asked to update multiple construction environments with -# SDK information. When doing this, we check on-disk for whether -# the SDK has 'mfc' and 'atl' subdirectories. Since going to disk -# is expensive, cache results by directory. - -SDKEnvironmentUpdates = {} - -def set_sdk_by_directory(env, sdk_dir): - global SDKEnvironmentUpdates - debug('set_sdk_by_directory: Using dir:%s'%sdk_dir) - try: - env_tuple_list = SDKEnvironmentUpdates[sdk_dir] - except KeyError: - env_tuple_list = [] - SDKEnvironmentUpdates[sdk_dir] = env_tuple_list - - include_path = os.path.join(sdk_dir, 'include') - mfc_path = os.path.join(include_path, 'mfc') - atl_path = os.path.join(include_path, 'atl') - - if os.path.exists(mfc_path): - env_tuple_list.append(('INCLUDE', mfc_path)) - if os.path.exists(atl_path): - env_tuple_list.append(('INCLUDE', atl_path)) - env_tuple_list.append(('INCLUDE', include_path)) - - env_tuple_list.append(('LIB', os.path.join(sdk_dir, 'lib'))) - env_tuple_list.append(('LIBPATH', os.path.join(sdk_dir, 'lib'))) - env_tuple_list.append(('PATH', os.path.join(sdk_dir, 'bin'))) - - for variable, directory in env_tuple_list: - env.PrependENVPath(variable, directory) - - -# TODO(sgk): currently unused; remove? -def get_cur_sdk_dir_from_reg(): - """Try to find the platform sdk directory from the registry. - - Return None if failed or the directory does not exist""" - if not SCons.Util.can_read_reg: - debug('SCons cannot read registry') - return None - - try: - val = common.read_reg(_CURINSTALLED_SDK_HKEY_ROOT) - debug("Found current sdk dir in registry: %s" % val) - except WindowsError, e: - debug("Did not find current sdk in registry") - return None - - if not os.path.exists(val): - debug("Current sdk dir %s not on fs" % val) - return None - - return val - -def get_sdk_by_version(mssdk): - if mssdk not in SupportedSDKMap: - msg = "SDK version %s is not supported" % repr(mssdk) - raise SCons.Errors.UserError(msg) - get_installed_sdks() - return InstalledSDKMap.get(mssdk) - -def get_default_sdk(): - """Set up the default Platform/Windows SDK.""" - get_installed_sdks() - if not InstalledSDKList: - return None - return InstalledSDKList[0] - - - - -def mssdk_setup_env(env): - debug('sdk.py:mssdk_setup_env()') - if 'MSSDK_DIR' in env: - sdk_dir = env['MSSDK_DIR'] - if sdk_dir is None: - return - sdk_dir = env.subst(sdk_dir) - debug('sdk.py:mssdk_setup_env: Using MSSDK_DIR:%s'%sdk_dir) - elif 'MSSDK_VERSION' in env: - sdk_version = env['MSSDK_VERSION'] - if sdk_version is None: - msg = "SDK version %s is not installed" % repr(mssdk) - raise SCons.Errors.UserError(msg) - sdk_version = env.subst(sdk_version) - mssdk = get_sdk_by_version(sdk_version) - sdk_dir = mssdk.get_sdk_dir() - debug('sdk.py:mssdk_setup_env: Using MSSDK_VERSION:%s'%sdk_dir) - elif 'MSVS_VERSION' in env: - msvs_version = env['MSVS_VERSION'] - debug('sdk.py:mssdk_setup_env:Getting MSVS_VERSION from env:%s'%msvs_version) - if msvs_version is None: - debug('sdk.py:mssdk_setup_env thinks msvs_version is None') - return - msvs_version = env.subst(msvs_version) - import vs - msvs = vs.get_vs_by_version(msvs_version) - debug('sdk.py:mssdk_setup_env:msvs is :%s'%msvs) - if not msvs: - debug('sdk.py:mssdk_setup_env: no VS version detected, bailingout:%s'%msvs) - return - sdk_version = msvs.sdk_version - debug('sdk.py:msvs.sdk_version is %s'%sdk_version) - if not sdk_version: - return - mssdk = get_sdk_by_version(sdk_version) - if not mssdk: - mssdk = get_default_sdk() - if not mssdk: - return - sdk_dir = mssdk.get_sdk_dir() - debug('sdk.py:mssdk_setup_env: Using MSVS_VERSION:%s'%sdk_dir) - else: - mssdk = get_default_sdk() - if not mssdk: - return - sdk_dir = mssdk.get_sdk_dir() - debug('sdk.py:mssdk_setup_env: not using any env values. sdk_dir:%s'%sdk_dir) - - set_sdk_by_directory(env, sdk_dir) - - #print "No MSVS_VERSION: this is likely to be a bug" - -def mssdk_exists(version=None): - sdks = get_installed_sdks() - if version is None: - return len(sdks) > 0 - return version in sdks - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/MSCommon/vc.py b/scons/scons-local-2.2.0/SCons/Tool/MSCommon/vc.py deleted file mode 100644 index 9bbec2170..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/MSCommon/vc.py +++ /dev/null @@ -1,464 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -# TODO: -# * supported arch for versions: for old versions of batch file without -# argument, giving bogus argument cannot be detected, so we have to hardcode -# this here -# * print warning when msvc version specified but not found -# * find out why warning do not print -# * test on 64 bits XP + VS 2005 (and VS 6 if possible) -# * SDK -# * Assembly -__revision__ = "src/engine/SCons/Tool/MSCommon/vc.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -__doc__ = """Module for Visual C/C++ detection and configuration. -""" -import SCons.compat - -import os -import platform -from string import digits as string_digits - -import SCons.Warnings - -import common - -debug = common.debug - -import sdk - -get_installed_sdks = sdk.get_installed_sdks - - -class VisualCException(Exception): - pass - -class UnsupportedVersion(VisualCException): - pass - -class UnsupportedArch(VisualCException): - pass - -class MissingConfiguration(VisualCException): - pass - -class NoVersionFound(VisualCException): - pass - -class BatchFileExecutionError(VisualCException): - pass - -# Dict to 'canonalize' the arch -_ARCH_TO_CANONICAL = { - "amd64" : "amd64", - "emt64" : "amd64", - "i386" : "x86", - "i486" : "x86", - "i586" : "x86", - "i686" : "x86", - "ia64" : "ia64", - "itanium" : "ia64", - "x86" : "x86", - "x86_64" : "amd64", -} - -# Given a (host, target) tuple, return the argument for the bat file. Both host -# and targets should be canonalized. -_HOST_TARGET_ARCH_TO_BAT_ARCH = { - ("x86", "x86"): "x86", - ("x86", "amd64"): "x86_amd64", - ("amd64", "amd64"): "amd64", - ("amd64", "x86"): "x86", - ("x86", "ia64"): "x86_ia64" -} - -def get_host_target(env): - debug('vc.py:get_host_target()') - - host_platform = env.get('HOST_ARCH') - if not host_platform: - host_platform = platform.machine() - # TODO(2.5): the native Python platform.machine() function returns - # '' on all Python versions before 2.6, after which it also uses - # PROCESSOR_ARCHITECTURE. - if not host_platform: - host_platform = os.environ.get('PROCESSOR_ARCHITECTURE', '') - - # Retain user requested TARGET_ARCH - req_target_platform = env.get('TARGET_ARCH') - debug('vc.py:get_host_target() req_target_platform:%s'%req_target_platform) - - if req_target_platform: - # If user requested a specific platform then only try that one. - target_platform = req_target_platform - else: - target_platform = host_platform - - try: - host = _ARCH_TO_CANONICAL[host_platform.lower()] - except KeyError, e: - msg = "Unrecognized host architecture %s" - raise ValueError(msg % repr(host_platform)) - - try: - target = _ARCH_TO_CANONICAL[target_platform.lower()] - except KeyError, e: - all_archs = str(_ARCH_TO_CANONICAL.keys()) - raise ValueError("Unrecognized target architecture %s\n\tValid architectures: %s" % (target_platform, all_archs)) - - return (host, target,req_target_platform) - -_VCVER = ["11.0", "11.0Exp", "10.0", "10.0Exp", "9.0", "9.0Exp","8.0", "8.0Exp","7.1", "7.0", "6.0"] - -_VCVER_TO_PRODUCT_DIR = { - '11.0': [ - r'Microsoft\VisualStudio\11.0\Setup\VC\ProductDir'], - '11.0Exp' : [ - r'Microsoft\VCExpress\11.0\Setup\VC\ProductDir'], - '10.0': [ - r'Microsoft\VisualStudio\10.0\Setup\VC\ProductDir'], - '10.0Exp' : [ - r'Microsoft\VCExpress\10.0\Setup\VC\ProductDir'], - '9.0': [ - r'Microsoft\VisualStudio\9.0\Setup\VC\ProductDir'], - '9.0Exp' : [ - r'Microsoft\VCExpress\9.0\Setup\VC\ProductDir'], - '8.0': [ - r'Microsoft\VisualStudio\8.0\Setup\VC\ProductDir'], - '8.0Exp': [ - r'Microsoft\VCExpress\8.0\Setup\VC\ProductDir'], - '7.1': [ - r'Microsoft\VisualStudio\7.1\Setup\VC\ProductDir'], - '7.0': [ - r'Microsoft\VisualStudio\7.0\Setup\VC\ProductDir'], - '6.0': [ - r'Microsoft\VisualStudio\6.0\Setup\Microsoft Visual C++\ProductDir'] -} - -def msvc_version_to_maj_min(msvc_version): - msvc_version_numeric = ''.join([x for x in msvc_version if x in string_digits + '.']) - - t = msvc_version_numeric.split(".") - if not len(t) == 2: - raise ValueError("Unrecognized version %s (%s)" % (msvc_version,msvc_version_numeric)) - try: - maj = int(t[0]) - min = int(t[1]) - return maj, min - except ValueError, e: - raise ValueError("Unrecognized version %s (%s)" % (msvc_version,msvc_version_numeric)) - -def is_host_target_supported(host_target, msvc_version): - """Return True if the given (host, target) tuple is supported given the - msvc version. - - Parameters - ---------- - host_target: tuple - tuple of (canonalized) host-target, e.g. ("x86", "amd64") for cross - compilation from 32 bits windows to 64 bits. - msvc_version: str - msvc version (major.minor, e.g. 10.0) - - Note - ---- - This only check whether a given version *may* support the given (host, - target), not that the toolchain is actually present on the machine. - """ - # We assume that any Visual Studio version supports x86 as a target - if host_target[1] != "x86": - maj, min = msvc_version_to_maj_min(msvc_version) - if maj < 8: - return False - - return True - -def find_vc_pdir(msvc_version): - """Try to find the product directory for the given - version. - - Note - ---- - If for some reason the requested version could not be found, an - exception which inherits from VisualCException will be raised.""" - root = 'Software\\' - if common.is_win64(): - root = root + 'Wow6432Node\\' - try: - hkeys = _VCVER_TO_PRODUCT_DIR[msvc_version] - except KeyError: - debug("Unknown version of MSVC: %s" % msvc_version) - raise UnsupportedVersion("Unknown version %s" % msvc_version) - - for key in hkeys: - key = root + key - try: - comps = common.read_reg(key) - except WindowsError, e: - debug('find_vc_dir(): no VC registry key %s' % repr(key)) - else: - debug('find_vc_dir(): found VC in registry: %s' % comps) - if os.path.exists(comps): - return comps - else: - debug('find_vc_dir(): reg says dir is %s, but it does not exist. (ignoring)'\ - % comps) - raise MissingConfiguration("registry dir %s not found on the filesystem" % comps) - return None - -def find_batch_file(env,msvc_version,host_arch,target_arch): - """ - Find the location of the batch script which should set up the compiler - for any TARGET_ARCH whose compilers were installed by Visual Studio/VCExpress - """ - pdir = find_vc_pdir(msvc_version) - if pdir is None: - raise NoVersionFound("No version of Visual Studio found") - - debug('vc.py: find_batch_file() pdir:%s'%pdir) - - # filter out e.g. "Exp" from the version name - msvc_ver_numeric = ''.join([x for x in msvc_version if x in string_digits + "."]) - vernum = float(msvc_ver_numeric) - if 7 <= vernum < 8: - pdir = os.path.join(pdir, os.pardir, "Common7", "Tools") - batfilename = os.path.join(pdir, "vsvars32.bat") - elif vernum < 7: - pdir = os.path.join(pdir, "Bin") - batfilename = os.path.join(pdir, "vcvars32.bat") - else: # >= 8 - batfilename = os.path.join(pdir, "vcvarsall.bat") - - if not os.path.exists(batfilename): - debug("Not found: %s" % batfilename) - batfilename = None - - installed_sdks=get_installed_sdks() - for _sdk in installed_sdks: - sdk_bat_file=_sdk.get_sdk_vc_script(host_arch,target_arch) - sdk_bat_file_path=os.path.join(pdir,sdk_bat_file) - debug('vc.py:find_batch_file() sdk_bat_file_path:%s'%sdk_bat_file_path) - if os.path.exists(sdk_bat_file_path): - return (batfilename,sdk_bat_file_path) - else: - debug("vc.py:find_batch_file() not found:%s"%sdk_bat_file_path) - else: - return (batfilename,None) - -__INSTALLED_VCS_RUN = None - -def cached_get_installed_vcs(): - global __INSTALLED_VCS_RUN - - if __INSTALLED_VCS_RUN is None: - ret = get_installed_vcs() - __INSTALLED_VCS_RUN = ret - - return __INSTALLED_VCS_RUN - -def get_installed_vcs(): - installed_versions = [] - for ver in _VCVER: - debug('trying to find VC %s' % ver) - try: - if find_vc_pdir(ver): - debug('found VC %s' % ver) - installed_versions.append(ver) - else: - debug('find_vc_pdir return None for ver %s' % ver) - except VisualCException, e: - debug('did not find VC %s: caught exception %s' % (ver, str(e))) - return installed_versions - -def reset_installed_vcs(): - """Make it try again to find VC. This is just for the tests.""" - __INSTALLED_VCS_RUN = None - -def script_env(script, args=None): - stdout = common.get_output(script, args) - # Stupid batch files do not set return code: we take a look at the - # beginning of the output for an error message instead - olines = stdout.splitlines() - if olines[0].startswith("The specified configuration type is missing"): - raise BatchFileExecutionError("\n".join(olines[:2])) - - return common.parse_output(stdout) - -def get_default_version(env): - debug('get_default_version()') - - msvc_version = env.get('MSVC_VERSION') - msvs_version = env.get('MSVS_VERSION') - - debug('get_default_version(): msvc_version:%s msvs_version:%s'%(msvc_version,msvs_version)) - - if msvs_version and not msvc_version: - SCons.Warnings.warn( - SCons.Warnings.DeprecatedWarning, - "MSVS_VERSION is deprecated: please use MSVC_VERSION instead ") - return msvs_version - elif msvc_version and msvs_version: - if not msvc_version == msvs_version: - SCons.Warnings.warn( - SCons.Warnings.VisualVersionMismatch, - "Requested msvc version (%s) and msvs version (%s) do " \ - "not match: please use MSVC_VERSION only to request a " \ - "visual studio version, MSVS_VERSION is deprecated" \ - % (msvc_version, msvs_version)) - return msvs_version - if not msvc_version: - installed_vcs = cached_get_installed_vcs() - debug('installed_vcs:%s' % installed_vcs) - if not installed_vcs: - #msg = 'No installed VCs' - #debug('msv %s\n' % repr(msg)) - #SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg) - debug('msvc_setup_env: No installed VCs') - return None - msvc_version = installed_vcs[0] - debug('msvc_setup_env: using default installed MSVC version %s\n' % repr(msvc_version)) - - return msvc_version - -def msvc_setup_env_once(env): - try: - has_run = env["MSVC_SETUP_RUN"] - except KeyError: - has_run = False - - if not has_run: - msvc_setup_env(env) - env["MSVC_SETUP_RUN"] = True - -def msvc_find_valid_batch_script(env,version): - debug('vc.py:msvc_find_valid_batch_script()') - # Find the host platform, target platform, and if present the requested - # target platform - (host_platform, target_platform,req_target_platform) = get_host_target(env) - - # If the user hasn't specifically requested a TARGET_ARCH, and - # The TARGET_ARCH is amd64 then also try 32 bits if there are no viable - # 64 bit tools installed - try_target_archs = [target_platform] - if not req_target_platform and target_platform in ('amd64','x86_64'): - try_target_archs.append('x86') - - d = None - for tp in try_target_archs: - # Set to current arch. - env['TARGET_ARCH']=tp - - debug("vc.py:msvc_find_valid_batch_script() trying target_platform:%s"%tp) - host_target = (host_platform, tp) - if not is_host_target_supported(host_target, version): - warn_msg = "host, target = %s not supported for MSVC version %s" % \ - (host_target, version) - SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) - arg = _HOST_TARGET_ARCH_TO_BAT_ARCH[host_target] - - # Try to locate a batch file for this host/target platform combo - try: - (vc_script,sdk_script) = find_batch_file(env,version,host_platform,tp) - debug('vc.py:msvc_find_valid_batch_script() vc_script:%s sdk_script:%s'%(vc_script,sdk_script)) - except VisualCException, e: - msg = str(e) - debug('Caught exception while looking for batch file (%s)' % msg) - warn_msg = "VC version %s not installed. " + \ - "C/C++ compilers are most likely not set correctly.\n" + \ - " Installed versions are: %s" - warn_msg = warn_msg % (version, cached_get_installed_vcs()) - SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) - continue - - # Try to use the located batch file for this host/target platform combo - debug('vc.py:msvc_find_valid_batch_script() use_script 2 %s, args:%s\n' % (repr(vc_script), arg)) - if vc_script: - try: - d = script_env(vc_script, args=arg) - except BatchFileExecutionError, e: - debug('vc.py:msvc_find_valid_batch_script() use_script 3: failed running VC script %s: %s: Error:%s'%(repr(vc_script),arg,e)) - vc_script=None - if not vc_script and sdk_script: - debug('vc.py:msvc_find_valid_batch_script() use_script 4: trying sdk script: %s'%(sdk_script)) - try: - d = script_env(sdk_script,args=[]) - except BatchFileExecutionError,e: - debug('vc.py:msvc_find_valid_batch_script() use_script 5: failed running SDK script %s: Error:%s'%(repr(sdk_script),e)) - continue - elif not vc_script and not sdk_script: - debug('vc.py:msvc_find_valid_batch_script() use_script 6: Neither VC script nor SDK script found') - continue - - # If we cannot find a viable installed compiler, reset the TARGET_ARCH - # To it's initial value - if not d: - env['TARGET_ARCH']=req_target_platform - - return d - - -def msvc_setup_env(env): - debug('msvc_setup_env()') - - version = get_default_version(env) - if version is None: - warn_msg = "No version of Visual Studio compiler found - C/C++ " \ - "compilers most likely not set correctly" - SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) - return None - debug('msvc_setup_env: using specified MSVC version %s\n' % repr(version)) - - # XXX: we set-up both MSVS version for backward - # compatibility with the msvs tool - env['MSVC_VERSION'] = version - env['MSVS_VERSION'] = version - env['MSVS'] = {} - - - use_script = env.get('MSVC_USE_SCRIPT', True) - if SCons.Util.is_String(use_script): - debug('vc.py:msvc_setup_env() use_script 1 %s\n' % repr(use_script)) - d = script_env(use_script) - elif use_script: - d = msvc_find_valid_batch_script(env,version) - debug('vc.py:msvc_setup_env() use_script 2 %s\n' % d) - if not d: - return d - else: - debug('MSVC_USE_SCRIPT set to False') - warn_msg = "MSVC_USE_SCRIPT set to False, assuming environment " \ - "set correctly." - SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) - return None - - for k, v in d.items(): - debug('vc.py:msvc_setup_env() env:%s -> %s'%(k,v)) - env.PrependENVPath(k, v, delete_existing=True) - -def msvc_exists(version=None): - vcs = cached_get_installed_vcs() - if version is None: - return len(vcs) > 0 - return version in vcs - diff --git a/scons/scons-local-2.2.0/SCons/Tool/MSCommon/vs.py b/scons/scons-local-2.2.0/SCons/Tool/MSCommon/vs.py deleted file mode 100644 index f5feb2a44..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/MSCommon/vs.py +++ /dev/null @@ -1,553 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/MSCommon/vs.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -__doc__ = """Module to detect Visual Studio and/or Visual C/C++ -""" - -import os - -import SCons.Errors -import SCons.Util - -from common import debug, \ - get_output, \ - is_win64, \ - normalize_env, \ - parse_output, \ - read_reg - -import SCons.Tool.MSCommon.vc - -class VisualStudio(object): - """ - An abstract base class for trying to find installed versions of - Visual Studio. - """ - def __init__(self, version, **kw): - self.version = version - kw['vc_version'] = kw.get('vc_version', version) - kw['sdk_version'] = kw.get('sdk_version', version) - self.__dict__.update(kw) - self._cache = {} - - # - - def find_batch_file(self): - vs_dir = self.get_vs_dir() - if not vs_dir: - debug('find_executable(): no vs_dir') - return None - batch_file = os.path.join(vs_dir, self.batch_file_path) - batch_file = os.path.normpath(batch_file) - if not os.path.isfile(batch_file): - debug('find_batch_file(): %s not on file system' % batch_file) - return None - return batch_file - - def find_vs_dir_by_vc(self): - SCons.Tool.MSCommon.vc.get_installed_vcs() - dir = SCons.Tool.MSCommon.vc.find_vc_pdir(self.vc_version) - if not dir: - debug('find_vs_dir(): no installed VC %s' % self.vc_version) - return None - return dir - - def find_vs_dir_by_reg(self): - root = 'Software\\' - - if is_win64(): - root = root + 'Wow6432Node\\' - for key in self.hkeys: - if key=='use_dir': - return self.find_vs_dir_by_vc() - key = root + key - try: - comps = read_reg(key) - except WindowsError, e: - debug('find_vs_dir_by_reg(): no VS registry key %s' % repr(key)) - else: - debug('find_vs_dir_by_reg(): found VS in registry: %s' % comps) - return comps - return None - - def find_vs_dir(self): - """ Can use registry or location of VC to find vs dir - First try to find by registry, and if that fails find via VC dir - """ - - - if True: - vs_dir=self.find_vs_dir_by_reg() - return vs_dir - else: - return self.find_vs_dir_by_vc() - - def find_executable(self): - vs_dir = self.get_vs_dir() - if not vs_dir: - debug('find_executable(): no vs_dir (%s)'%vs_dir) - return None - executable = os.path.join(vs_dir, self.executable_path) - executable = os.path.normpath(executable) - if not os.path.isfile(executable): - debug('find_executable(): %s not on file system' % executable) - return None - return executable - - # - - def get_batch_file(self): - try: - return self._cache['batch_file'] - except KeyError: - batch_file = self.find_batch_file() - self._cache['batch_file'] = batch_file - return batch_file - - def get_executable(self): - try: - debug('get_executable using cache:%s'%self._cache['executable']) - return self._cache['executable'] - except KeyError: - executable = self.find_executable() - self._cache['executable'] = executable - debug('get_executable not in cache:%s'%executable) - return executable - - def get_vs_dir(self): - try: - return self._cache['vs_dir'] - except KeyError: - vs_dir = self.find_vs_dir() - self._cache['vs_dir'] = vs_dir - return vs_dir - - def get_supported_arch(self): - try: - return self._cache['supported_arch'] - except KeyError: - # RDEVE: for the time being use hardcoded lists - # supported_arch = self.find_supported_arch() - self._cache['supported_arch'] = self.supported_arch - return self.supported_arch - - def reset(self): - self._cache = {} - -# The list of supported Visual Studio versions we know how to detect. -# -# How to look for .bat file ? -# - VS 2008 Express (x86): -# * from registry key productdir, gives the full path to vsvarsall.bat. In -# HKEY_LOCAL_MACHINE): -# Software\Microsoft\VCEpress\9.0\Setup\VC\productdir -# * from environmnent variable VS90COMNTOOLS: the path is then ..\..\VC -# relatively to the path given by the variable. -# -# - VS 2008 Express (WoW6432: 32 bits on windows x64): -# Software\Wow6432Node\Microsoft\VCEpress\9.0\Setup\VC\productdir -# -# - VS 2005 Express (x86): -# * from registry key productdir, gives the full path to vsvarsall.bat. In -# HKEY_LOCAL_MACHINE): -# Software\Microsoft\VCEpress\8.0\Setup\VC\productdir -# * from environmnent variable VS80COMNTOOLS: the path is then ..\..\VC -# relatively to the path given by the variable. -# -# - VS 2005 Express (WoW6432: 32 bits on windows x64): does not seem to have a -# productdir ? -# -# - VS 2003 .Net (pro edition ? x86): -# * from registry key productdir. The path is then ..\Common7\Tools\ -# relatively to the key. The key is in HKEY_LOCAL_MACHINE): -# Software\Microsoft\VisualStudio\7.1\Setup\VC\productdir -# * from environmnent variable VS71COMNTOOLS: the path is the full path to -# vsvars32.bat -# -# - VS 98 (VS 6): -# * from registry key productdir. The path is then Bin -# relatively to the key. The key is in HKEY_LOCAL_MACHINE): -# Software\Microsoft\VisualStudio\6.0\Setup\VC98\productdir -# -# The first version found in the list is the one used by default if -# there are multiple versions installed. Barring good reasons to -# the contrary, this means we should list versions from most recent -# to oldest. Pro versions get listed before Express versions on the -# assumption that, by default, you'd rather use the version you paid -# good money for in preference to whatever Microsoft makes available -# for free. -# -# If you update this list, update the documentation in Tool/msvs.xml. - -SupportedVSList = [ - # Visual Studio 2010 - # TODO: find the settings, perhaps from someone with a CTP copy? - #VisualStudio('TBD', - # hkey_root=r'TBD', - # common_tools_var='TBD', - # executable_path=r'TBD', - # default_dirname='TBD', - #), - - # Visual Studio 11 - # The batch file we look for is in the VC directory, - # so the devenv.com executable is up in ..\..\Common7\IDE. - VisualStudio('11.0', - sdk_version='6.1', - hkeys=[r'Microsoft\VisualStudio\11.0\Setup\VS\ProductDir'], - common_tools_var='VS110COMNTOOLS', - executable_path=r'Common7\IDE\devenv.com', - batch_file_path=r'Common7\Tools\vsvars32.bat', - default_dirname='Microsoft Visual Studio 11', - supported_arch=['x86', 'amd64'], - ), - - # Visual C++ 11 Express Edition - # The batch file we look for is in the VC directory, - # so the VCExpress.exe executable is up in ..\..\Common7\IDE. - VisualStudio('11.0Exp', - vc_version='11.0', - sdk_version='6.1', - hkeys=[r'Microsoft\VCExpress\11.0\Setup\VS\ProductDir'], - common_tools_var='VS110COMNTOOLS', - executable_path=r'Common7\IDE\VCExpress.exe', - batch_file_path=r'Common7\Tools\vsvars32.bat', - default_dirname='Microsoft Visual Studio 11', - supported_arch=['x86'], - ), - - # Visual Studio 2010 - # The batch file we look for is in the VC directory, - # so the devenv.com executable is up in ..\..\Common7\IDE. - VisualStudio('10.0', - sdk_version='6.1', - hkeys=[r'Microsoft\VisualStudio\10.0\Setup\VS\ProductDir'], - common_tools_var='VS100COMNTOOLS', - executable_path=r'Common7\IDE\devenv.com', - batch_file_path=r'Common7\Tools\vsvars32.bat', - default_dirname='Microsoft Visual Studio 10', - supported_arch=['x86', 'amd64'], - ), - - # Visual C++ 2010 Express Edition - # The batch file we look for is in the VC directory, - # so the VCExpress.exe executable is up in ..\..\Common7\IDE. - VisualStudio('10.0Exp', - vc_version='10.0', - sdk_version='6.1', - hkeys=[r'Microsoft\VCExpress\10.0\Setup\VS\ProductDir'], - common_tools_var='VS100COMNTOOLS', - executable_path=r'Common7\IDE\VCExpress.exe', - batch_file_path=r'Common7\Tools\vsvars32.bat', - default_dirname='Microsoft Visual Studio 10', - supported_arch=['x86'], - ), - - # Visual Studio 2008 - # The batch file we look for is in the VC directory, - # so the devenv.com executable is up in ..\..\Common7\IDE. - VisualStudio('9.0', - sdk_version='6.1', - hkeys=[r'Microsoft\VisualStudio\9.0\Setup\VS\ProductDir'], - common_tools_var='VS90COMNTOOLS', - executable_path=r'Common7\IDE\devenv.com', - batch_file_path=r'Common7\Tools\vsvars32.bat', - default_dirname='Microsoft Visual Studio 9', - supported_arch=['x86', 'amd64'], - ), - - # Visual C++ 2008 Express Edition - # The batch file we look for is in the VC directory, - # so the VCExpress.exe executable is up in ..\..\Common7\IDE. - VisualStudio('9.0Exp', - vc_version='9.0', - sdk_version='6.1', - hkeys=[r'Microsoft\VCExpress\9.0\Setup\VS\ProductDir'], - common_tools_var='VS90COMNTOOLS', - executable_path=r'Common7\IDE\VCExpress.exe', - batch_file_path=r'Common7\Tools\vsvars32.bat', - default_dirname='Microsoft Visual Studio 9', - supported_arch=['x86'], - ), - - # Visual Studio 2005 - # The batch file we look for is in the VC directory, - # so the devenv.com executable is up in ..\..\Common7\IDE. - VisualStudio('8.0', - sdk_version='6.0A', - hkeys=[r'Microsoft\VisualStudio\8.0\Setup\VS\ProductDir'], - common_tools_var='VS80COMNTOOLS', - executable_path=r'Common7\IDE\devenv.com', - batch_file_path=r'Common7\Tools\vsvars32.bat', - default_dirname='Microsoft Visual Studio 8', - supported_arch=['x86', 'amd64'], - ), - - # Visual C++ 2005 Express Edition - # The batch file we look for is in the VC directory, - # so the VCExpress.exe executable is up in ..\..\Common7\IDE. - VisualStudio('8.0Exp', - vc_version='8.0Exp', - sdk_version='6.0A', - hkeys=[r'Microsoft\VCExpress\8.0\Setup\VS\ProductDir'], - common_tools_var='VS80COMNTOOLS', - executable_path=r'Common7\IDE\VCExpress.exe', - batch_file_path=r'Common7\Tools\vsvars32.bat', - default_dirname='Microsoft Visual Studio 8', - supported_arch=['x86'], - ), - - # Visual Studio .NET 2003 - # The batch file we look for is in the Common7\Tools directory, - # so the devenv.com executable is next door in ..\IDE. - VisualStudio('7.1', - sdk_version='6.0', - hkeys=[r'Microsoft\VisualStudio\7.1\Setup\VS\ProductDir'], - common_tools_var='VS71COMNTOOLS', - executable_path=r'Common7\IDE\devenv.com', - batch_file_path=r'Common7\Tools\vsvars32.bat', - default_dirname='Microsoft Visual Studio .NET 2003', - supported_arch=['x86'], - ), - - # Visual Studio .NET - # The batch file we look for is in the Common7\Tools directory, - # so the devenv.com executable is next door in ..\IDE. - VisualStudio('7.0', - sdk_version='2003R2', - hkeys=[r'Microsoft\VisualStudio\7.0\Setup\VS\ProductDir'], - common_tools_var='VS70COMNTOOLS', - executable_path=r'IDE\devenv.com', - batch_file_path=r'Common7\Tools\vsvars32.bat', - default_dirname='Microsoft Visual Studio .NET', - supported_arch=['x86'], - ), - - # Visual Studio 6.0 - VisualStudio('6.0', - sdk_version='2003R1', - hkeys=[r'Microsoft\VisualStudio\6.0\Setup\Microsoft Visual Studio\ProductDir', - 'use_dir'], - common_tools_var='VS60COMNTOOLS', - executable_path=r'Common\MSDev98\Bin\MSDEV.COM', - batch_file_path=r'Common7\Tools\vsvars32.bat', - default_dirname='Microsoft Visual Studio', - supported_arch=['x86'], - ), -] - -SupportedVSMap = {} -for vs in SupportedVSList: - SupportedVSMap[vs.version] = vs - - -# Finding installed versions of Visual Studio isn't cheap, because it -# goes not only to the registry but also to the disk to sanity-check -# that there is, in fact, a Visual Studio directory there and that the -# registry entry isn't just stale. Find this information once, when -# requested, and cache it. - -InstalledVSList = None -InstalledVSMap = None - -def get_installed_visual_studios(): - global InstalledVSList - global InstalledVSMap - if InstalledVSList is None: - InstalledVSList = [] - InstalledVSMap = {} - for vs in SupportedVSList: - debug('trying to find VS %s' % vs.version) - if vs.get_executable(): - debug('found VS %s' % vs.version) - InstalledVSList.append(vs) - InstalledVSMap[vs.version] = vs - return InstalledVSList - -def reset_installed_visual_studios(): - global InstalledVSList - global InstalledVSMap - InstalledVSList = None - InstalledVSMap = None - for vs in SupportedVSList: - vs.reset() - - # Need to clear installed VC's as well as they are used in finding - # installed VS's - SCons.Tool.MSCommon.vc.reset_installed_vcs() - - -# We may be asked to update multiple construction environments with -# SDK information. When doing this, we check on-disk for whether -# the SDK has 'mfc' and 'atl' subdirectories. Since going to disk -# is expensive, cache results by directory. - -#SDKEnvironmentUpdates = {} -# -#def set_sdk_by_directory(env, sdk_dir): -# global SDKEnvironmentUpdates -# try: -# env_tuple_list = SDKEnvironmentUpdates[sdk_dir] -# except KeyError: -# env_tuple_list = [] -# SDKEnvironmentUpdates[sdk_dir] = env_tuple_list -# -# include_path = os.path.join(sdk_dir, 'include') -# mfc_path = os.path.join(include_path, 'mfc') -# atl_path = os.path.join(include_path, 'atl') -# -# if os.path.exists(mfc_path): -# env_tuple_list.append(('INCLUDE', mfc_path)) -# if os.path.exists(atl_path): -# env_tuple_list.append(('INCLUDE', atl_path)) -# env_tuple_list.append(('INCLUDE', include_path)) -# -# env_tuple_list.append(('LIB', os.path.join(sdk_dir, 'lib'))) -# env_tuple_list.append(('LIBPATH', os.path.join(sdk_dir, 'lib'))) -# env_tuple_list.append(('PATH', os.path.join(sdk_dir, 'bin'))) -# -# for variable, directory in env_tuple_list: -# env.PrependENVPath(variable, directory) - -def msvs_exists(): - return (len(get_installed_visual_studios()) > 0) - -def get_vs_by_version(msvs): - global InstalledVSMap - global SupportedVSMap - - debug('vs.py:get_vs_by_version()') - if msvs not in SupportedVSMap: - msg = "Visual Studio version %s is not supported" % repr(msvs) - raise SCons.Errors.UserError(msg) - get_installed_visual_studios() - vs = InstalledVSMap.get(msvs) - debug('InstalledVSMap:%s'%InstalledVSMap) - debug('vs.py:get_vs_by_version: found vs:%s'%vs) - # Some check like this would let us provide a useful error message - # if they try to set a Visual Studio version that's not installed. - # However, we also want to be able to run tests (like the unit - # tests) on systems that don't, or won't ever, have it installed. - # It might be worth resurrecting this, with some configurable - # setting that the tests can use to bypass the check. - #if not vs: - # msg = "Visual Studio version %s is not installed" % repr(msvs) - # raise SCons.Errors.UserError, msg - return vs - -def get_default_version(env): - """Returns the default version string to use for MSVS. - - If no version was requested by the user through the MSVS environment - variable, query all the available the visual studios through - query_versions, and take the highest one. - - Return - ------ - version: str - the default version. - """ - if 'MSVS' not in env or not SCons.Util.is_Dict(env['MSVS']): - versions = [vs.version for vs in get_installed_visual_studios()] - env['MSVS'] = {'VERSIONS' : versions} - else: - versions = env['MSVS'].get('VERSIONS', []) - - if 'MSVS_VERSION' not in env: - if versions: - env['MSVS_VERSION'] = versions[0] #use highest version by default - else: - env['MSVS_VERSION'] = SupportedVSList[0].version - - env['MSVS']['VERSION'] = env['MSVS_VERSION'] - - return env['MSVS_VERSION'] - -def get_default_arch(env): - """Return the default arch to use for MSVS - - if no version was requested by the user through the MSVS_ARCH environment - variable, select x86 - - Return - ------ - arch: str - """ - arch = env.get('MSVS_ARCH', 'x86') - - msvs = InstalledVSMap.get(env['MSVS_VERSION']) - - if not msvs: - arch = 'x86' - elif not arch in msvs.get_supported_arch(): - fmt = "Visual Studio version %s does not support architecture %s" - raise SCons.Errors.UserError(fmt % (env['MSVS_VERSION'], arch)) - - return arch - -def merge_default_version(env): - version = get_default_version(env) - arch = get_default_arch(env) - -def msvs_setup_env(env): - batfilename = msvs.get_batch_file() - msvs = get_vs_by_version(version) - if msvs is None: - return - - # XXX: I think this is broken. This will silently set a bogus tool instead - # of failing, but there is no other way with the current scons tool - # framework - if batfilename is not None: - - vars = ('LIB', 'LIBPATH', 'PATH', 'INCLUDE') - - msvs_list = get_installed_visual_studios() - vscommonvarnames = [vs.common_tools_var for vs in msvs_list] - save_ENV = env['ENV'] - nenv = normalize_env(env['ENV'], - ['COMSPEC'] + vscommonvarnames, - force=True) - try: - output = get_output(batfilename, arch, env=nenv) - finally: - env['ENV'] = save_ENV - vars = parse_output(output, vars) - - for k, v in vars.items(): - env.PrependENVPath(k, v, delete_existing=1) - -def query_versions(): - """Query the system to get available versions of VS. A version is - considered when a batfile is found.""" - msvs_list = get_installed_visual_studios() - versions = [msvs.version for msvs in msvs_list] - return versions - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/Perforce.py b/scons/scons-local-2.2.0/SCons/Tool/Perforce.py deleted file mode 100644 index ade9e88a7..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/Perforce.py +++ /dev/null @@ -1,103 +0,0 @@ -"""SCons.Tool.Perforce.py - -Tool-specific initialization for Perforce Source Code Management system. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Tool/Perforce.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os - -import SCons.Action -import SCons.Builder -import SCons.Node.FS -import SCons.Util - -# This function should maybe be moved to SCons.Util? -from SCons.Tool.PharLapCommon import addPathIfNotExists - - -# Variables that we want to import from the base OS environment. -_import_env = [ 'P4PORT', 'P4CLIENT', 'P4USER', 'USER', 'USERNAME', 'P4PASSWD', - 'P4CHARSET', 'P4LANGUAGE', 'SystemRoot' ] - -PerforceAction = SCons.Action.Action('$P4COM', '$P4COMSTR') - -def generate(env): - """Add a Builder factory function and construction variables for - Perforce to an Environment.""" - - def PerforceFactory(env=env): - """ """ - import SCons.Warnings as W - W.warn(W.DeprecatedSourceCodeWarning, """The Perforce() factory is deprecated and there is no replacement.""") - return SCons.Builder.Builder(action = PerforceAction, env = env) - - #setattr(env, 'Perforce', PerforceFactory) - env.Perforce = PerforceFactory - - env['P4'] = 'p4' - env['P4FLAGS'] = SCons.Util.CLVar('') - env['P4COM'] = '$P4 $P4FLAGS sync $TARGET' - try: - environ = env['ENV'] - except KeyError: - environ = {} - env['ENV'] = environ - - # Perforce seems to use the PWD environment variable rather than - # calling getcwd() for itself, which is odd. If no PWD variable - # is present, p4 WILL call getcwd, but this seems to cause problems - # with good ol' Windows's tilde-mangling for long file names. - environ['PWD'] = env.Dir('#').get_abspath() - - for var in _import_env: - v = os.environ.get(var) - if v: - environ[var] = v - - if SCons.Util.can_read_reg: - # If we can read the registry, add the path to Perforce to our environment. - try: - k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, - 'Software\\Perforce\\environment') - val, tok = SCons.Util.RegQueryValueEx(k, 'P4INSTROOT') - addPathIfNotExists(environ, 'PATH', val) - except SCons.Util.RegError: - # Can't detect where Perforce is, hope the user has it set in the - # PATH. - pass - -def exists(env): - return env.Detect('p4') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/PharLapCommon.py b/scons/scons-local-2.2.0/SCons/Tool/PharLapCommon.py deleted file mode 100644 index 0f54a2ba9..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/PharLapCommon.py +++ /dev/null @@ -1,137 +0,0 @@ -"""SCons.Tool.PharLapCommon - -This module contains common code used by all Tools for the -Phar Lap ETS tool chain. Right now, this is linkloc and -386asm. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/PharLapCommon.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os -import os.path -import SCons.Errors -import SCons.Util -import re - -def getPharLapPath(): - """Reads the registry to find the installed path of the Phar Lap ETS - development kit. - - Raises UserError if no installed version of Phar Lap can - be found.""" - - if not SCons.Util.can_read_reg: - raise SCons.Errors.InternalError("No Windows registry module was found") - try: - k=SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, - 'SOFTWARE\\Pharlap\\ETS') - val, type = SCons.Util.RegQueryValueEx(k, 'BaseDir') - - # The following is a hack...there is (not surprisingly) - # an odd issue in the Phar Lap plug in that inserts - # a bunch of junk data after the phar lap path in the - # registry. We must trim it. - idx=val.find('\0') - if idx >= 0: - val = val[:idx] - - return os.path.normpath(val) - except SCons.Util.RegError: - raise SCons.Errors.UserError("Cannot find Phar Lap ETS path in the registry. Is it installed properly?") - -REGEX_ETS_VER = re.compile(r'#define\s+ETS_VER\s+([0-9]+)') - -def getPharLapVersion(): - """Returns the version of the installed ETS Tool Suite as a - decimal number. This version comes from the ETS_VER #define in - the embkern.h header. For example, '#define ETS_VER 1010' (which - is what Phar Lap 10.1 defines) would cause this method to return - 1010. Phar Lap 9.1 does not have such a #define, but this method - will return 910 as a default. - - Raises UserError if no installed version of Phar Lap can - be found.""" - - include_path = os.path.join(getPharLapPath(), os.path.normpath("include/embkern.h")) - if not os.path.exists(include_path): - raise SCons.Errors.UserError("Cannot find embkern.h in ETS include directory.\nIs Phar Lap ETS installed properly?") - mo = REGEX_ETS_VER.search(open(include_path, 'r').read()) - if mo: - return int(mo.group(1)) - # Default return for Phar Lap 9.1 - return 910 - -def addPathIfNotExists(env_dict, key, path, sep=os.pathsep): - """This function will take 'key' out of the dictionary - 'env_dict', then add the path 'path' to that key if it is not - already there. This treats the value of env_dict[key] as if it - has a similar format to the PATH variable...a list of paths - separated by tokens. The 'path' will get added to the list if it - is not already there.""" - try: - is_list = 1 - paths = env_dict[key] - if not SCons.Util.is_List(env_dict[key]): - paths = paths.split(sep) - is_list = 0 - if os.path.normcase(path) not in list(map(os.path.normcase, paths)): - paths = [ path ] + paths - if is_list: - env_dict[key] = paths - else: - env_dict[key] = sep.join(paths) - except KeyError: - env_dict[key] = path - -def addPharLapPaths(env): - """This function adds the path to the Phar Lap binaries, includes, - and libraries, if they are not already there.""" - ph_path = getPharLapPath() - - try: - env_dict = env['ENV'] - except KeyError: - env_dict = {} - env['ENV'] = env_dict - addPathIfNotExists(env_dict, 'PATH', - os.path.join(ph_path, 'bin')) - addPathIfNotExists(env_dict, 'INCLUDE', - os.path.join(ph_path, 'include')) - addPathIfNotExists(env_dict, 'LIB', - os.path.join(ph_path, 'lib')) - addPathIfNotExists(env_dict, 'LIB', - os.path.join(ph_path, os.path.normpath('lib/vclib'))) - - env['PHARLAP_PATH'] = getPharLapPath() - env['PHARLAP_VERSION'] = str(getPharLapVersion()) - - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/RCS.py b/scons/scons-local-2.2.0/SCons/Tool/RCS.py deleted file mode 100644 index cc33a4eea..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/RCS.py +++ /dev/null @@ -1,64 +0,0 @@ -"""SCons.Tool.RCS.py - -Tool-specific initialization for RCS. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Tool/RCS.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Action -import SCons.Builder -import SCons.Util - -def generate(env): - """Add a Builder factory function and construction variables for - RCS to an Environment.""" - - def RCSFactory(env=env): - """ """ - import SCons.Warnings as W - W.warn(W.DeprecatedSourceCodeWarning, """The RCS() factory is deprecated and there is no replacement.""") - act = SCons.Action.Action('$RCS_COCOM', '$RCS_COCOMSTR') - return SCons.Builder.Builder(action = act, env = env) - - #setattr(env, 'RCS', RCSFactory) - env.RCS = RCSFactory - - env['RCS'] = 'rcs' - env['RCS_CO'] = 'co' - env['RCS_COFLAGS'] = SCons.Util.CLVar('') - env['RCS_COCOM'] = '$RCS_CO $RCS_COFLAGS $TARGET' - -def exists(env): - return env.Detect('rcs') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/SCCS.py b/scons/scons-local-2.2.0/SCons/Tool/SCCS.py deleted file mode 100644 index 5e35a8758..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/SCCS.py +++ /dev/null @@ -1,64 +0,0 @@ -"""SCons.Tool.SCCS.py - -Tool-specific initialization for SCCS. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Tool/SCCS.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Action -import SCons.Builder -import SCons.Util - -def generate(env): - """Add a Builder factory function and construction variables for - SCCS to an Environment.""" - - def SCCSFactory(env=env): - """ """ - import SCons.Warnings as W - W.warn(W.DeprecatedSourceCodeWarning, """The SCCS() factory is deprecated and there is no replacement.""") - act = SCons.Action.Action('$SCCSCOM', '$SCCSCOMSTR') - return SCons.Builder.Builder(action = act, env = env) - - #setattr(env, 'SCCS', SCCSFactory) - env.SCCS = SCCSFactory - - env['SCCS'] = 'sccs' - env['SCCSFLAGS'] = SCons.Util.CLVar('') - env['SCCSGETFLAGS'] = SCons.Util.CLVar('') - env['SCCSCOM'] = '$SCCS $SCCSFLAGS get $SCCSGETFLAGS $TARGET' - -def exists(env): - return env.Detect('sccs') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/Subversion.py b/scons/scons-local-2.2.0/SCons/Tool/Subversion.py deleted file mode 100644 index 212850fe3..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/Subversion.py +++ /dev/null @@ -1,71 +0,0 @@ -"""SCons.Tool.Subversion.py - -Tool-specific initialization for Subversion. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Tool/Subversion.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os.path - -import SCons.Action -import SCons.Builder -import SCons.Util - -def generate(env): - """Add a Builder factory function and construction variables for - Subversion to an Environment.""" - - def SubversionFactory(repos, module='', env=env): - """ """ - # fail if repos is not an absolute path name? - import SCons.Warnings as W - W.warn(W.DeprecatedSourceCodeWarning, """The Subversion() factory is deprecated and there is no replacement.""") - if module != '': - module = os.path.join(module, '') - act = SCons.Action.Action('$SVNCOM', '$SVNCOMSTR') - return SCons.Builder.Builder(action = act, - env = env, - SVNREPOSITORY = repos, - SVNMODULE = module) - - #setattr(env, 'Subversion', SubversionFactory) - env.Subversion = SubversionFactory - - env['SVN'] = 'svn' - env['SVNFLAGS'] = SCons.Util.CLVar('') - env['SVNCOM'] = '$SVN $SVNFLAGS cat $SVNREPOSITORY/$SVNMODULE$TARGET > $TARGET' - -def exists(env): - return env.Detect('svn') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/__init__.py b/scons/scons-local-2.2.0/SCons/Tool/__init__.py deleted file mode 100644 index 5bee64dd9..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/__init__.py +++ /dev/null @@ -1,681 +0,0 @@ -"""SCons.Tool - -SCons tool selection. - -This looks for modules that define a callable object that can modify -a construction environment as appropriate for a given tool (or tool -chain). - -Note that because this subsystem just *selects* a callable that can -modify a construction environment, it's possible for people to define -their own "tool specification" in an arbitrary callable function. No -one needs to use or tie in to this subsystem in order to roll their own -tool definition. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Tool/__init__.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import imp -import sys - -import SCons.Builder -import SCons.Errors -import SCons.Node.FS -import SCons.Scanner -import SCons.Scanner.C -import SCons.Scanner.D -import SCons.Scanner.LaTeX -import SCons.Scanner.Prog - -DefaultToolpath=[] - -CScanner = SCons.Scanner.C.CScanner() -DScanner = SCons.Scanner.D.DScanner() -LaTeXScanner = SCons.Scanner.LaTeX.LaTeXScanner() -PDFLaTeXScanner = SCons.Scanner.LaTeX.PDFLaTeXScanner() -ProgramScanner = SCons.Scanner.Prog.ProgramScanner() -SourceFileScanner = SCons.Scanner.Base({}, name='SourceFileScanner') - -CSuffixes = [".c", ".C", ".cxx", ".cpp", ".c++", ".cc", - ".h", ".H", ".hxx", ".hpp", ".hh", - ".F", ".fpp", ".FPP", - ".m", ".mm", - ".S", ".spp", ".SPP", ".sx"] - -DSuffixes = ['.d'] - -IDLSuffixes = [".idl", ".IDL"] - -LaTeXSuffixes = [".tex", ".ltx", ".latex"] - -for suffix in CSuffixes: - SourceFileScanner.add_scanner(suffix, CScanner) - -for suffix in DSuffixes: - SourceFileScanner.add_scanner(suffix, DScanner) - -# FIXME: what should be done here? Two scanners scan the same extensions, -# but look for different files, e.g., "picture.eps" vs. "picture.pdf". -# The builders for DVI and PDF explicitly reference their scanners -# I think that means this is not needed??? -for suffix in LaTeXSuffixes: - SourceFileScanner.add_scanner(suffix, LaTeXScanner) - SourceFileScanner.add_scanner(suffix, PDFLaTeXScanner) - -class Tool(object): - def __init__(self, name, toolpath=[], **kw): - self.name = name - self.toolpath = toolpath + DefaultToolpath - # remember these so we can merge them into the call - self.init_kw = kw - - module = self._tool_module() - self.generate = module.generate - self.exists = module.exists - if hasattr(module, 'options'): - self.options = module.options - - def _tool_module(self): - # TODO: Interchange zipimport with normal initilization for better error reporting - oldpythonpath = sys.path - sys.path = self.toolpath + sys.path - - try: - try: - file, path, desc = imp.find_module(self.name, self.toolpath) - try: - return imp.load_module(self.name, file, path, desc) - finally: - if file: - file.close() - except ImportError, e: - if str(e)!="No module named %s"%self.name: - raise SCons.Errors.EnvironmentError(e) - try: - import zipimport - except ImportError: - pass - else: - for aPath in self.toolpath: - try: - importer = zipimport.zipimporter(aPath) - return importer.load_module(self.name) - except ImportError, e: - pass - finally: - sys.path = oldpythonpath - - full_name = 'SCons.Tool.' + self.name - try: - return sys.modules[full_name] - except KeyError: - try: - smpath = sys.modules['SCons.Tool'].__path__ - try: - file, path, desc = imp.find_module(self.name, smpath) - module = imp.load_module(full_name, file, path, desc) - setattr(SCons.Tool, self.name, module) - if file: - file.close() - return module - except ImportError, e: - if str(e)!="No module named %s"%self.name: - raise SCons.Errors.EnvironmentError(e) - try: - import zipimport - importer = zipimport.zipimporter( sys.modules['SCons.Tool'].__path__[0] ) - module = importer.load_module(full_name) - setattr(SCons.Tool, self.name, module) - return module - except ImportError, e: - m = "No tool named '%s': %s" % (self.name, e) - raise SCons.Errors.EnvironmentError(m) - except ImportError, e: - m = "No tool named '%s': %s" % (self.name, e) - raise SCons.Errors.EnvironmentError(m) - - def __call__(self, env, *args, **kw): - if self.init_kw is not None: - # Merge call kws into init kws; - # but don't bash self.init_kw. - if kw is not None: - call_kw = kw - kw = self.init_kw.copy() - kw.update(call_kw) - else: - kw = self.init_kw - env.Append(TOOLS = [ self.name ]) - if hasattr(self, 'options'): - import SCons.Variables - if 'options' not in env: - from SCons.Script import ARGUMENTS - env['options']=SCons.Variables.Variables(args=ARGUMENTS) - opts=env['options'] - - self.options(opts) - opts.Update(env) - - self.generate(env, *args, **kw) - - def __str__(self): - return self.name - -########################################################################## -# Create common executable program / library / object builders - -def createProgBuilder(env): - """This is a utility function that creates the Program - Builder in an Environment if it is not there already. - - If it is already there, we return the existing one. - """ - - try: - program = env['BUILDERS']['Program'] - except KeyError: - import SCons.Defaults - program = SCons.Builder.Builder(action = SCons.Defaults.LinkAction, - emitter = '$PROGEMITTER', - prefix = '$PROGPREFIX', - suffix = '$PROGSUFFIX', - src_suffix = '$OBJSUFFIX', - src_builder = 'Object', - target_scanner = ProgramScanner) - env['BUILDERS']['Program'] = program - - return program - -def createStaticLibBuilder(env): - """This is a utility function that creates the StaticLibrary - Builder in an Environment if it is not there already. - - If it is already there, we return the existing one. - """ - - try: - static_lib = env['BUILDERS']['StaticLibrary'] - except KeyError: - action_list = [ SCons.Action.Action("$ARCOM", "$ARCOMSTR") ] - if env.Detect('ranlib'): - ranlib_action = SCons.Action.Action("$RANLIBCOM", "$RANLIBCOMSTR") - action_list.append(ranlib_action) - - static_lib = SCons.Builder.Builder(action = action_list, - emitter = '$LIBEMITTER', - prefix = '$LIBPREFIX', - suffix = '$LIBSUFFIX', - src_suffix = '$OBJSUFFIX', - src_builder = 'StaticObject') - env['BUILDERS']['StaticLibrary'] = static_lib - env['BUILDERS']['Library'] = static_lib - - return static_lib - -def createSharedLibBuilder(env): - """This is a utility function that creates the SharedLibrary - Builder in an Environment if it is not there already. - - If it is already there, we return the existing one. - """ - - try: - shared_lib = env['BUILDERS']['SharedLibrary'] - except KeyError: - import SCons.Defaults - action_list = [ SCons.Defaults.SharedCheck, - SCons.Defaults.ShLinkAction ] - shared_lib = SCons.Builder.Builder(action = action_list, - emitter = "$SHLIBEMITTER", - prefix = '$SHLIBPREFIX', - suffix = '$SHLIBSUFFIX', - target_scanner = ProgramScanner, - src_suffix = '$SHOBJSUFFIX', - src_builder = 'SharedObject') - env['BUILDERS']['SharedLibrary'] = shared_lib - - return shared_lib - -def createLoadableModuleBuilder(env): - """This is a utility function that creates the LoadableModule - Builder in an Environment if it is not there already. - - If it is already there, we return the existing one. - """ - - try: - ld_module = env['BUILDERS']['LoadableModule'] - except KeyError: - import SCons.Defaults - action_list = [ SCons.Defaults.SharedCheck, - SCons.Defaults.LdModuleLinkAction ] - ld_module = SCons.Builder.Builder(action = action_list, - emitter = "$LDMODULEEMITTER", - prefix = '$LDMODULEPREFIX', - suffix = '$LDMODULESUFFIX', - target_scanner = ProgramScanner, - src_suffix = '$SHOBJSUFFIX', - src_builder = 'SharedObject') - env['BUILDERS']['LoadableModule'] = ld_module - - return ld_module - -def createObjBuilders(env): - """This is a utility function that creates the StaticObject - and SharedObject Builders in an Environment if they - are not there already. - - If they are there already, we return the existing ones. - - This is a separate function because soooo many Tools - use this functionality. - - The return is a 2-tuple of (StaticObject, SharedObject) - """ - - - try: - static_obj = env['BUILDERS']['StaticObject'] - except KeyError: - static_obj = SCons.Builder.Builder(action = {}, - emitter = {}, - prefix = '$OBJPREFIX', - suffix = '$OBJSUFFIX', - src_builder = ['CFile', 'CXXFile'], - source_scanner = SourceFileScanner, - single_source = 1) - env['BUILDERS']['StaticObject'] = static_obj - env['BUILDERS']['Object'] = static_obj - - try: - shared_obj = env['BUILDERS']['SharedObject'] - except KeyError: - shared_obj = SCons.Builder.Builder(action = {}, - emitter = {}, - prefix = '$SHOBJPREFIX', - suffix = '$SHOBJSUFFIX', - src_builder = ['CFile', 'CXXFile'], - source_scanner = SourceFileScanner, - single_source = 1) - env['BUILDERS']['SharedObject'] = shared_obj - - return (static_obj, shared_obj) - -def createCFileBuilders(env): - """This is a utility function that creates the CFile/CXXFile - Builders in an Environment if they - are not there already. - - If they are there already, we return the existing ones. - - This is a separate function because soooo many Tools - use this functionality. - - The return is a 2-tuple of (CFile, CXXFile) - """ - - try: - c_file = env['BUILDERS']['CFile'] - except KeyError: - c_file = SCons.Builder.Builder(action = {}, - emitter = {}, - suffix = {None:'$CFILESUFFIX'}) - env['BUILDERS']['CFile'] = c_file - - env.SetDefault(CFILESUFFIX = '.c') - - try: - cxx_file = env['BUILDERS']['CXXFile'] - except KeyError: - cxx_file = SCons.Builder.Builder(action = {}, - emitter = {}, - suffix = {None:'$CXXFILESUFFIX'}) - env['BUILDERS']['CXXFile'] = cxx_file - env.SetDefault(CXXFILESUFFIX = '.cc') - - return (c_file, cxx_file) - -########################################################################## -# Create common Java builders - -def CreateJarBuilder(env): - try: - java_jar = env['BUILDERS']['Jar'] - except KeyError: - fs = SCons.Node.FS.get_default_fs() - jar_com = SCons.Action.Action('$JARCOM', '$JARCOMSTR') - java_jar = SCons.Builder.Builder(action = jar_com, - suffix = '$JARSUFFIX', - src_suffix = '$JAVACLASSSUFIX', - src_builder = 'JavaClassFile', - source_factory = fs.Entry) - env['BUILDERS']['Jar'] = java_jar - return java_jar - -def CreateJavaHBuilder(env): - try: - java_javah = env['BUILDERS']['JavaH'] - except KeyError: - fs = SCons.Node.FS.get_default_fs() - java_javah_com = SCons.Action.Action('$JAVAHCOM', '$JAVAHCOMSTR') - java_javah = SCons.Builder.Builder(action = java_javah_com, - src_suffix = '$JAVACLASSSUFFIX', - target_factory = fs.Entry, - source_factory = fs.File, - src_builder = 'JavaClassFile') - env['BUILDERS']['JavaH'] = java_javah - return java_javah - -def CreateJavaClassFileBuilder(env): - try: - java_class_file = env['BUILDERS']['JavaClassFile'] - except KeyError: - fs = SCons.Node.FS.get_default_fs() - javac_com = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR') - java_class_file = SCons.Builder.Builder(action = javac_com, - emitter = {}, - #suffix = '$JAVACLASSSUFFIX', - src_suffix = '$JAVASUFFIX', - src_builder = ['JavaFile'], - target_factory = fs.Entry, - source_factory = fs.File) - env['BUILDERS']['JavaClassFile'] = java_class_file - return java_class_file - -def CreateJavaClassDirBuilder(env): - try: - java_class_dir = env['BUILDERS']['JavaClassDir'] - except KeyError: - fs = SCons.Node.FS.get_default_fs() - javac_com = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR') - java_class_dir = SCons.Builder.Builder(action = javac_com, - emitter = {}, - target_factory = fs.Dir, - source_factory = fs.Dir) - env['BUILDERS']['JavaClassDir'] = java_class_dir - return java_class_dir - -def CreateJavaFileBuilder(env): - try: - java_file = env['BUILDERS']['JavaFile'] - except KeyError: - java_file = SCons.Builder.Builder(action = {}, - emitter = {}, - suffix = {None:'$JAVASUFFIX'}) - env['BUILDERS']['JavaFile'] = java_file - env['JAVASUFFIX'] = '.java' - return java_file - -class ToolInitializerMethod(object): - """ - This is added to a construction environment in place of a - method(s) normally called for a Builder (env.Object, env.StaticObject, - etc.). When called, it has its associated ToolInitializer - object search the specified list of tools and apply the first - one that exists to the construction environment. It then calls - whatever builder was (presumably) added to the construction - environment in place of this particular instance. - """ - def __init__(self, name, initializer): - """ - Note: we store the tool name as __name__ so it can be used by - the class that attaches this to a construction environment. - """ - self.__name__ = name - self.initializer = initializer - - def get_builder(self, env): - """ - Returns the appropriate real Builder for this method name - after having the associated ToolInitializer object apply - the appropriate Tool module. - """ - builder = getattr(env, self.__name__) - - self.initializer.apply_tools(env) - - builder = getattr(env, self.__name__) - if builder is self: - # There was no Builder added, which means no valid Tool - # for this name was found (or possibly there's a mismatch - # between the name we were called by and the Builder name - # added by the Tool module). - return None - - self.initializer.remove_methods(env) - - return builder - - def __call__(self, env, *args, **kw): - """ - """ - builder = self.get_builder(env) - if builder is None: - return [], [] - return builder(*args, **kw) - -class ToolInitializer(object): - """ - A class for delayed initialization of Tools modules. - - Instances of this class associate a list of Tool modules with - a list of Builder method names that will be added by those Tool - modules. As part of instantiating this object for a particular - construction environment, we also add the appropriate - ToolInitializerMethod objects for the various Builder methods - that we want to use to delay Tool searches until necessary. - """ - def __init__(self, env, tools, names): - if not SCons.Util.is_List(tools): - tools = [tools] - if not SCons.Util.is_List(names): - names = [names] - self.env = env - self.tools = tools - self.names = names - self.methods = {} - for name in names: - method = ToolInitializerMethod(name, self) - self.methods[name] = method - env.AddMethod(method) - - def remove_methods(self, env): - """ - Removes the methods that were added by the tool initialization - so we no longer copy and re-bind them when the construction - environment gets cloned. - """ - for method in self.methods.values(): - env.RemoveMethod(method) - - def apply_tools(self, env): - """ - Searches the list of associated Tool modules for one that - exists, and applies that to the construction environment. - """ - for t in self.tools: - tool = SCons.Tool.Tool(t) - if tool.exists(env): - env.Tool(tool) - return - - # If we fall through here, there was no tool module found. - # This is where we can put an informative error message - # about the inability to find the tool. We'll start doing - # this as we cut over more pre-defined Builder+Tools to use - # the ToolInitializer class. - -def Initializers(env): - ToolInitializer(env, ['install'], ['_InternalInstall', '_InternalInstallAs']) - def Install(self, *args, **kw): - return self._InternalInstall(*args, **kw) - def InstallAs(self, *args, **kw): - return self._InternalInstallAs(*args, **kw) - env.AddMethod(Install) - env.AddMethod(InstallAs) - -def FindTool(tools, env): - for tool in tools: - t = Tool(tool) - if t.exists(env): - return tool - return None - -def FindAllTools(tools, env): - def ToolExists(tool, env=env): - return Tool(tool).exists(env) - return list(filter (ToolExists, tools)) - -def tool_list(platform, env): - - other_plat_tools=[] - # XXX this logic about what tool to prefer on which platform - # should be moved into either the platform files or - # the tool files themselves. - # The search orders here are described in the man page. If you - # change these search orders, update the man page as well. - if str(platform) == 'win32': - "prefer Microsoft tools on Windows" - linkers = ['mslink', 'gnulink', 'ilink', 'linkloc', 'ilink32' ] - c_compilers = ['msvc', 'mingw', 'gcc', 'intelc', 'icl', 'icc', 'cc', 'bcc32' ] - cxx_compilers = ['msvc', 'intelc', 'icc', 'g++', 'c++', 'bcc32' ] - assemblers = ['masm', 'nasm', 'gas', '386asm' ] - fortran_compilers = ['gfortran', 'g77', 'ifl', 'cvf', 'f95', 'f90', 'fortran'] - ars = ['mslib', 'ar', 'tlib'] - other_plat_tools=['msvs','midl'] - elif str(platform) == 'os2': - "prefer IBM tools on OS/2" - linkers = ['ilink', 'gnulink', ]#'mslink'] - c_compilers = ['icc', 'gcc',]# 'msvc', 'cc'] - cxx_compilers = ['icc', 'g++',]# 'msvc', 'c++'] - assemblers = ['nasm',]# 'masm', 'gas'] - fortran_compilers = ['ifl', 'g77'] - ars = ['ar',]# 'mslib'] - elif str(platform) == 'irix': - "prefer MIPSPro on IRIX" - linkers = ['sgilink', 'gnulink'] - c_compilers = ['sgicc', 'gcc', 'cc'] - cxx_compilers = ['sgic++', 'g++', 'c++'] - assemblers = ['as', 'gas'] - fortran_compilers = ['f95', 'f90', 'f77', 'g77', 'fortran'] - ars = ['sgiar'] - elif str(platform) == 'sunos': - "prefer Forte tools on SunOS" - linkers = ['sunlink', 'gnulink'] - c_compilers = ['suncc', 'gcc', 'cc'] - cxx_compilers = ['sunc++', 'g++', 'c++'] - assemblers = ['as', 'gas'] - fortran_compilers = ['sunf95', 'sunf90', 'sunf77', 'f95', 'f90', 'f77', - 'gfortran', 'g77', 'fortran'] - ars = ['sunar'] - elif str(platform) == 'hpux': - "prefer aCC tools on HP-UX" - linkers = ['hplink', 'gnulink'] - c_compilers = ['hpcc', 'gcc', 'cc'] - cxx_compilers = ['hpc++', 'g++', 'c++'] - assemblers = ['as', 'gas'] - fortran_compilers = ['f95', 'f90', 'f77', 'g77', 'fortran'] - ars = ['ar'] - elif str(platform) == 'aix': - "prefer AIX Visual Age tools on AIX" - linkers = ['aixlink', 'gnulink'] - c_compilers = ['aixcc', 'gcc', 'cc'] - cxx_compilers = ['aixc++', 'g++', 'c++'] - assemblers = ['as', 'gas'] - fortran_compilers = ['f95', 'f90', 'aixf77', 'g77', 'fortran'] - ars = ['ar'] - elif str(platform) == 'darwin': - "prefer GNU tools on Mac OS X, except for some linkers and IBM tools" - linkers = ['applelink', 'gnulink'] - c_compilers = ['gcc', 'cc'] - cxx_compilers = ['g++', 'c++'] - assemblers = ['as'] - fortran_compilers = ['gfortran', 'f95', 'f90', 'g77'] - ars = ['ar'] - else: - "prefer GNU tools on all other platforms" - linkers = ['gnulink', 'mslink', 'ilink'] - c_compilers = ['gcc', 'msvc', 'intelc', 'icc', 'cc'] - cxx_compilers = ['g++', 'msvc', 'intelc', 'icc', 'c++'] - assemblers = ['gas', 'nasm', 'masm'] - fortran_compilers = ['gfortran', 'g77', 'ifort', 'ifl', 'f95', 'f90', 'f77'] - ars = ['ar', 'mslib'] - - c_compiler = FindTool(c_compilers, env) or c_compilers[0] - - # XXX this logic about what tool provides what should somehow be - # moved into the tool files themselves. - if c_compiler and c_compiler == 'mingw': - # MinGW contains a linker, C compiler, C++ compiler, - # Fortran compiler, archiver and assembler: - cxx_compiler = None - linker = None - assembler = None - fortran_compiler = None - ar = None - else: - # Don't use g++ if the C compiler has built-in C++ support: - if c_compiler in ('msvc', 'intelc', 'icc'): - cxx_compiler = None - else: - cxx_compiler = FindTool(cxx_compilers, env) or cxx_compilers[0] - linker = FindTool(linkers, env) or linkers[0] - assembler = FindTool(assemblers, env) or assemblers[0] - fortran_compiler = FindTool(fortran_compilers, env) or fortran_compilers[0] - ar = FindTool(ars, env) or ars[0] - - other_tools = FindAllTools(other_plat_tools + [ - 'dmd', - #TODO: merge 'install' into 'filesystem' and - # make 'filesystem' the default - 'filesystem', - 'm4', - 'wix', #'midl', 'msvs', - # Parser generators - 'lex', 'yacc', - # Foreign function interface - 'rpcgen', 'swig', - # Java - 'jar', 'javac', 'javah', 'rmic', - # TeX - 'dvipdf', 'dvips', 'gs', - 'tex', 'latex', 'pdflatex', 'pdftex', - # Archivers - 'tar', 'zip', 'rpm', - # SourceCode factories - 'BitKeeper', 'CVS', 'Perforce', - 'RCS', 'SCCS', # 'Subversion', - ], env) - - tools = ([linker, c_compiler, cxx_compiler, - fortran_compiler, assembler, ar] - + other_tools) - - return [x for x in tools if x] - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/aixc++.py b/scons/scons-local-2.2.0/SCons/Tool/aixc++.py deleted file mode 100644 index fecfe766f..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/aixc++.py +++ /dev/null @@ -1,82 +0,0 @@ -"""SCons.Tool.aixc++ - -Tool-specific initialization for IBM xlC / Visual Age C++ compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/aixc++.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os.path - -import SCons.Platform.aix - -cplusplus = __import__('c++', globals(), locals(), []) - -packages = ['vacpp.cmp.core', 'vacpp.cmp.batch', 'vacpp.cmp.C', 'ibmcxx.cmp'] - -def get_xlc(env): - xlc = env.get('CXX', 'xlC') - xlc_r = env.get('SHCXX', 'xlC_r') - return SCons.Platform.aix.get_xlc(env, xlc, xlc_r, packages) - -def smart_cxxflags(source, target, env, for_signature): - build_dir = env.GetBuildPath() - if build_dir: - return '-qtempinc=' + os.path.join(build_dir, 'tempinc') - return '' - -def generate(env): - """Add Builders and construction variables for xlC / Visual Age - suite to an Environment.""" - path, _cxx, _shcxx, version = get_xlc(env) - if path: - _cxx = os.path.join(path, _cxx) - _shcxx = os.path.join(path, _shcxx) - - cplusplus.generate(env) - - env['CXX'] = _cxx - env['SHCXX'] = _shcxx - env['CXXVERSION'] = version - env['SHOBJSUFFIX'] = '.pic.o' - -def exists(env): - path, _cxx, _shcxx, version = get_xlc(env) - if path and _cxx: - xlc = os.path.join(path, _cxx) - if os.path.exists(xlc): - return xlc - return None - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/aixcc.py b/scons/scons-local-2.2.0/SCons/Tool/aixcc.py deleted file mode 100644 index d611fdcf0..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/aixcc.py +++ /dev/null @@ -1,74 +0,0 @@ -"""SCons.Tool.aixcc - -Tool-specific initialization for IBM xlc / Visual Age C compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/aixcc.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os.path - -import SCons.Platform.aix - -import cc - -packages = ['vac.C', 'ibmcxx.cmp'] - -def get_xlc(env): - xlc = env.get('CC', 'xlc') - xlc_r = env.get('SHCC', 'xlc_r') - return SCons.Platform.aix.get_xlc(env, xlc, xlc_r, packages) - -def generate(env): - """Add Builders and construction variables for xlc / Visual Age - suite to an Environment.""" - path, _cc, _shcc, version = get_xlc(env) - if path: - _cc = os.path.join(path, _cc) - _shcc = os.path.join(path, _shcc) - - cc.generate(env) - - env['CC'] = _cc - env['SHCC'] = _shcc - env['CCVERSION'] = version - -def exists(env): - path, _cc, _shcc, version = get_xlc(env) - if path and _cc: - xlc = os.path.join(path, _cc) - if os.path.exists(xlc): - return xlc - return None - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/aixf77.py b/scons/scons-local-2.2.0/SCons/Tool/aixf77.py deleted file mode 100644 index c3e062e00..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/aixf77.py +++ /dev/null @@ -1,80 +0,0 @@ -"""engine.SCons.Tool.aixf77 - -Tool-specific initialization for IBM Visual Age f77 Fortran compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/aixf77.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os.path - -#import SCons.Platform.aix - -import f77 - -# It would be good to look for the AIX F77 package the same way we're now -# looking for the C and C++ packages. This should be as easy as supplying -# the correct package names in the following list and uncommenting the -# SCons.Platform.aix_get_xlc() call the in the function below. -packages = [] - -def get_xlf77(env): - xlf77 = env.get('F77', 'xlf77') - xlf77_r = env.get('SHF77', 'xlf77_r') - #return SCons.Platform.aix.get_xlc(env, xlf77, xlf77_r, packages) - return (None, xlf77, xlf77_r, None) - -def generate(env): - """ - Add Builders and construction variables for the Visual Age FORTRAN - compiler to an Environment. - """ - path, _f77, _shf77, version = get_xlf77(env) - if path: - _f77 = os.path.join(path, _f77) - _shf77 = os.path.join(path, _shf77) - - f77.generate(env) - - env['F77'] = _f77 - env['SHF77'] = _shf77 - -def exists(env): - path, _f77, _shf77, version = get_xlf77(env) - if path and _f77: - xlf77 = os.path.join(path, _f77) - if os.path.exists(xlf77): - return xlf77 - return None - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/aixlink.py b/scons/scons-local-2.2.0/SCons/Tool/aixlink.py deleted file mode 100644 index 3a064bd2c..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/aixlink.py +++ /dev/null @@ -1,76 +0,0 @@ -"""SCons.Tool.aixlink - -Tool-specific initialization for the IBM Visual Age linker. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/aixlink.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os -import os.path - -import SCons.Util - -import aixcc -import link - -cplusplus = __import__('c++', globals(), locals(), []) - -def smart_linkflags(source, target, env, for_signature): - if cplusplus.iscplusplus(source): - build_dir = env.subst('$BUILDDIR', target=target, source=source) - if build_dir: - return '-qtempinc=' + os.path.join(build_dir, 'tempinc') - return '' - -def generate(env): - """ - Add Builders and construction variables for Visual Age linker to - an Environment. - """ - link.generate(env) - - env['SMARTLINKFLAGS'] = smart_linkflags - env['LINKFLAGS'] = SCons.Util.CLVar('$SMARTLINKFLAGS') - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -qmkshrobj -qsuppress=1501-218') - env['SHLIBSUFFIX'] = '.a' - -def exists(env): - path, _cc, _shcc, version = aixcc.get_xlc(env) - if path and _cc: - xlc = os.path.join(path, _cc) - if os.path.exists(xlc): - return xlc - return None - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/applelink.py b/scons/scons-local-2.2.0/SCons/Tool/applelink.py deleted file mode 100644 index 7b0cc1768..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/applelink.py +++ /dev/null @@ -1,71 +0,0 @@ -"""SCons.Tool.applelink - -Tool-specific initialization for the Apple gnu-like linker. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/applelink.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Util - -# Even though the Mac is based on the GNU toolchain, it doesn't understand -# the -rpath option, so we use the "link" tool instead of "gnulink". -import link - -def generate(env): - """Add Builders and construction variables for applelink to an - Environment.""" - link.generate(env) - - env['FRAMEWORKPATHPREFIX'] = '-F' - env['_FRAMEWORKPATH'] = '${_concat(FRAMEWORKPATHPREFIX, FRAMEWORKPATH, "", __env__)}' - env['_FRAMEWORKS'] = '${_concat("-framework ", FRAMEWORKS, "", __env__)}' - env['LINKCOM'] = env['LINKCOM'] + ' $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -dynamiclib') - env['SHLINKCOM'] = env['SHLINKCOM'] + ' $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' - - # override the default for loadable modules, which are different - # on OS X than dynamic shared libs. echoing what XCode does for - # pre/suffixes: - env['LDMODULEPREFIX'] = '' - env['LDMODULESUFFIX'] = '' - env['LDMODULEFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -bundle') - env['LDMODULECOM'] = '$LDMODULE -o ${TARGET} $LDMODULEFLAGS $SOURCES $_LIBDIRFLAGS $_LIBFLAGS $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' - - - -def exists(env): - return env['PLATFORM'] == 'darwin' - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/ar.py b/scons/scons-local-2.2.0/SCons/Tool/ar.py deleted file mode 100644 index 655e56b60..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/ar.py +++ /dev/null @@ -1,63 +0,0 @@ -"""SCons.Tool.ar - -Tool-specific initialization for ar (library archive). - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/ar.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Defaults -import SCons.Tool -import SCons.Util - - -def generate(env): - """Add Builders and construction variables for ar to an Environment.""" - SCons.Tool.createStaticLibBuilder(env) - - env['AR'] = 'ar' - env['ARFLAGS'] = SCons.Util.CLVar('rc') - env['ARCOM'] = '$AR $ARFLAGS $TARGET $SOURCES' - env['LIBPREFIX'] = 'lib' - env['LIBSUFFIX'] = '.a' - - if env.Detect('ranlib'): - env['RANLIB'] = 'ranlib' - env['RANLIBFLAGS'] = SCons.Util.CLVar('') - env['RANLIBCOM'] = '$RANLIB $RANLIBFLAGS $TARGET' - -def exists(env): - return env.Detect('ar') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/as.py b/scons/scons-local-2.2.0/SCons/Tool/as.py deleted file mode 100644 index 6275d8136..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/as.py +++ /dev/null @@ -1,78 +0,0 @@ -"""SCons.Tool.as - -Tool-specific initialization for as, the generic Posix assembler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/as.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Defaults -import SCons.Tool -import SCons.Util - -assemblers = ['as'] - -ASSuffixes = ['.s', '.asm', '.ASM'] -ASPPSuffixes = ['.spp', '.SPP', '.sx'] -if SCons.Util.case_sensitive_suffixes('.s', '.S'): - ASPPSuffixes.extend(['.S']) -else: - ASSuffixes.extend(['.S']) - -def generate(env): - """Add Builders and construction variables for as to an Environment.""" - static_obj, shared_obj = SCons.Tool.createObjBuilders(env) - - for suffix in ASSuffixes: - static_obj.add_action(suffix, SCons.Defaults.ASAction) - shared_obj.add_action(suffix, SCons.Defaults.ASAction) - static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) - shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) - - for suffix in ASPPSuffixes: - static_obj.add_action(suffix, SCons.Defaults.ASPPAction) - shared_obj.add_action(suffix, SCons.Defaults.ASPPAction) - static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) - shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) - - env['AS'] = env.Detect(assemblers) or 'as' - env['ASFLAGS'] = SCons.Util.CLVar('') - env['ASCOM'] = '$AS $ASFLAGS -o $TARGET $SOURCES' - env['ASPPFLAGS'] = '$ASFLAGS' - env['ASPPCOM'] = '$CC $ASPPFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES' - -def exists(env): - return env.Detect(assemblers) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/bcc32.py b/scons/scons-local-2.2.0/SCons/Tool/bcc32.py deleted file mode 100644 index c426e79e1..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/bcc32.py +++ /dev/null @@ -1,81 +0,0 @@ -"""SCons.Tool.bcc32 - -XXX - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/bcc32.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os -import os.path - -import SCons.Defaults -import SCons.Tool -import SCons.Util - -def findIt(program, env): - # First search in the SCons path and then the OS path: - borwin = env.WhereIs(program) or SCons.Util.WhereIs(program) - if borwin: - dir = os.path.dirname(borwin) - env.PrependENVPath('PATH', dir) - return borwin - -def generate(env): - findIt('bcc32', env) - """Add Builders and construction variables for bcc to an - Environment.""" - static_obj, shared_obj = SCons.Tool.createObjBuilders(env) - for suffix in ['.c', '.cpp']: - static_obj.add_action(suffix, SCons.Defaults.CAction) - shared_obj.add_action(suffix, SCons.Defaults.ShCAction) - static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) - shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) - - env['CC'] = 'bcc32' - env['CCFLAGS'] = SCons.Util.CLVar('') - env['CFLAGS'] = SCons.Util.CLVar('') - env['CCCOM'] = '$CC -q $CFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o$TARGET $SOURCES' - env['SHCC'] = '$CC' - env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') - env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS') - env['SHCCCOM'] = '$SHCC -WD $SHCFLAGS $SHCCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o$TARGET $SOURCES' - env['CPPDEFPREFIX'] = '-D' - env['CPPDEFSUFFIX'] = '' - env['INCPREFIX'] = '-I' - env['INCSUFFIX'] = '' - env['SHOBJSUFFIX'] = '.dll' - env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 0 - env['CFILESUFFIX'] = '.cpp' - -def exists(env): - return findIt('bcc32', env) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/c++.py b/scons/scons-local-2.2.0/SCons/Tool/c++.py deleted file mode 100644 index 8cd1dea29..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/c++.py +++ /dev/null @@ -1,99 +0,0 @@ -"""SCons.Tool.c++ - -Tool-specific initialization for generic Posix C++ compilers. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/c++.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os.path - -import SCons.Tool -import SCons.Defaults -import SCons.Util - -compilers = ['CC', 'c++'] - -CXXSuffixes = ['.cpp', '.cc', '.cxx', '.c++', '.C++', '.mm'] -if SCons.Util.case_sensitive_suffixes('.c', '.C'): - CXXSuffixes.append('.C') - -def iscplusplus(source): - if not source: - # Source might be None for unusual cases like SConf. - return 0 - for s in source: - if s.sources: - ext = os.path.splitext(str(s.sources[0]))[1] - if ext in CXXSuffixes: - return 1 - return 0 - -def generate(env): - """ - Add Builders and construction variables for Visual Age C++ compilers - to an Environment. - """ - import SCons.Tool - import SCons.Tool.cc - static_obj, shared_obj = SCons.Tool.createObjBuilders(env) - - for suffix in CXXSuffixes: - static_obj.add_action(suffix, SCons.Defaults.CXXAction) - shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction) - static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) - shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) - - SCons.Tool.cc.add_common_cc_variables(env) - - env['CXX'] = 'c++' - env['CXXFLAGS'] = SCons.Util.CLVar('') - env['CXXCOM'] = '$CXX -o $TARGET -c $CXXFLAGS $CCFLAGS $_CCCOMCOM $SOURCES' - env['SHCXX'] = '$CXX' - env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') - env['SHCXXCOM'] = '$SHCXX -o $TARGET -c $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM $SOURCES' - - env['CPPDEFPREFIX'] = '-D' - env['CPPDEFSUFFIX'] = '' - env['INCPREFIX'] = '-I' - env['INCSUFFIX'] = '' - env['SHOBJSUFFIX'] = '.os' - env['OBJSUFFIX'] = '.o' - env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 0 - - env['CXXFILESUFFIX'] = '.cc' - -def exists(env): - return env.Detect(compilers) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/cc.py b/scons/scons-local-2.2.0/SCons/Tool/cc.py deleted file mode 100644 index 806f25131..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/cc.py +++ /dev/null @@ -1,102 +0,0 @@ -"""SCons.Tool.cc - -Tool-specific initialization for generic Posix C compilers. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/cc.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Tool -import SCons.Defaults -import SCons.Util - -CSuffixes = ['.c', '.m'] -if not SCons.Util.case_sensitive_suffixes('.c', '.C'): - CSuffixes.append('.C') - -def add_common_cc_variables(env): - """ - Add underlying common "C compiler" variables that - are used by multiple tools (specifically, c++). - """ - if '_CCCOMCOM' not in env: - env['_CCCOMCOM'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS' - # It's a hack to test for darwin here, but the alternative - # of creating an applecc.py to contain this seems overkill. - # Maybe someday the Apple platform will require more setup and - # this logic will be moved. - env['FRAMEWORKS'] = SCons.Util.CLVar('') - env['FRAMEWORKPATH'] = SCons.Util.CLVar('') - if env['PLATFORM'] == 'darwin': - env['_CCCOMCOM'] = env['_CCCOMCOM'] + ' $_FRAMEWORKPATH' - - if 'CCFLAGS' not in env: - env['CCFLAGS'] = SCons.Util.CLVar('') - - if 'SHCCFLAGS' not in env: - env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') - -def generate(env): - """ - Add Builders and construction variables for C compilers to an Environment. - """ - static_obj, shared_obj = SCons.Tool.createObjBuilders(env) - - for suffix in CSuffixes: - static_obj.add_action(suffix, SCons.Defaults.CAction) - shared_obj.add_action(suffix, SCons.Defaults.ShCAction) - static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) - shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) - - add_common_cc_variables(env) - - env['CC'] = 'cc' - env['CFLAGS'] = SCons.Util.CLVar('') - env['CCCOM'] = '$CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCCOMCOM $SOURCES' - env['SHCC'] = '$CC' - env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS') - env['SHCCCOM'] = '$SHCC -o $TARGET -c $SHCFLAGS $SHCCFLAGS $_CCCOMCOM $SOURCES' - - env['CPPDEFPREFIX'] = '-D' - env['CPPDEFSUFFIX'] = '' - env['INCPREFIX'] = '-I' - env['INCSUFFIX'] = '' - env['SHOBJSUFFIX'] = '.os' - env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 0 - - env['CFILESUFFIX'] = '.c' - -def exists(env): - return env.Detect('cc') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/cvf.py b/scons/scons-local-2.2.0/SCons/Tool/cvf.py deleted file mode 100644 index 2dcf195e1..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/cvf.py +++ /dev/null @@ -1,58 +0,0 @@ -"""engine.SCons.Tool.cvf - -Tool-specific initialization for the Compaq Visual Fortran compiler. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/cvf.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import fortran - -compilers = ['f90'] - -def generate(env): - """Add Builders and construction variables for compaq visual fortran to an Environment.""" - - fortran.generate(env) - - env['FORTRAN'] = 'f90' - env['FORTRANCOM'] = '$FORTRAN $FORTRANFLAGS $_FORTRANMODFLAG $_FORTRANINCFLAGS /compile_only ${SOURCES.windows} /object:${TARGET.windows}' - env['FORTRANPPCOM'] = '$FORTRAN $FORTRANFLAGS $CPPFLAGS $_CPPDEFFLAGS $_FORTRANMODFLAG $_FORTRANINCFLAGS /compile_only ${SOURCES.windows} /object:${TARGET.windows}' - env['SHFORTRANCOM'] = '$SHFORTRAN $SHFORTRANFLAGS $_FORTRANMODFLAG $_FORTRANINCFLAGS /compile_only ${SOURCES.windows} /object:${TARGET.windows}' - env['SHFORTRANPPCOM'] = '$SHFORTRAN $SHFORTRANFLAGS $CPPFLAGS $_CPPDEFFLAGS $_FORTRANMODFLAG $_FORTRANINCFLAGS /compile_only ${SOURCES.windows} /object:${TARGET.windows}' - env['OBJSUFFIX'] = '.obj' - env['FORTRANMODDIR'] = '${TARGET.dir}' - env['FORTRANMODDIRPREFIX'] = '/module:' - env['FORTRANMODDIRSUFFIX'] = '' - -def exists(env): - return env.Detect(compilers) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/default.py b/scons/scons-local-2.2.0/SCons/Tool/default.py deleted file mode 100644 index 292bd0ba3..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/default.py +++ /dev/null @@ -1,50 +0,0 @@ -"""SCons.Tool.default - -Initialization with a default tool list. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/default.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Tool - -def generate(env): - """Add default tools.""" - for t in SCons.Tool.tool_list(env['PLATFORM'], env): - SCons.Tool.Tool(t)(env) - -def exists(env): - return 1 - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/dmd.py b/scons/scons-local-2.2.0/SCons/Tool/dmd.py deleted file mode 100644 index 839020f0c..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/dmd.py +++ /dev/null @@ -1,240 +0,0 @@ -"""SCons.Tool.dmd - -Tool-specific initialization for the Digital Mars D compiler. -(http://digitalmars.com/d) - -Coded by Andy Friesen (andy@ikagames.com) -15 November 2003 - -Amended by Russel Winder (russel@russel.org.uk) -2010-02-07 - -There are a number of problems with this script at this point in time. -The one that irritates me the most is the Windows linker setup. The D -linker doesn't have a way to add lib paths on the commandline, as far -as I can see. You have to specify paths relative to the SConscript or -use absolute paths. To hack around it, add '#/blah'. This will link -blah.lib from the directory where SConstruct resides. - -Compiler variables: - DC - The name of the D compiler to use. Defaults to dmd or gdmd, - whichever is found. - DPATH - List of paths to search for import modules. - DVERSIONS - List of version tags to enable when compiling. - DDEBUG - List of debug tags to enable when compiling. - -Linker related variables: - LIBS - List of library files to link in. - DLINK - Name of the linker to use. Defaults to dmd or gdmd. - DLINKFLAGS - List of linker flags. - -Lib tool variables: - DLIB - Name of the lib tool to use. Defaults to lib. - DLIBFLAGS - List of flags to pass to the lib tool. - LIBS - Same as for the linker. (libraries to pull into the .lib) -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/dmd.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os - -import SCons.Action -import SCons.Builder -import SCons.Defaults -import SCons.Scanner.D -import SCons.Tool - -# Adapted from c++.py -def isD(source): - if not source: - return 0 - - for s in source: - if s.sources: - ext = os.path.splitext(str(s.sources[0]))[1] - if ext == '.d': - return 1 - return 0 - -smart_link = {} - -smart_lib = {} - -def generate(env): - global smart_link - global smart_lib - - static_obj, shared_obj = SCons.Tool.createObjBuilders(env) - - DAction = SCons.Action.Action('$DCOM', '$DCOMSTR') - - static_obj.add_action('.d', DAction) - shared_obj.add_action('.d', DAction) - static_obj.add_emitter('.d', SCons.Defaults.StaticObjectEmitter) - shared_obj.add_emitter('.d', SCons.Defaults.SharedObjectEmitter) - - dc = env.Detect(['dmd', 'gdmd']) - env['DC'] = dc - env['DCOM'] = '$DC $_DINCFLAGS $_DVERFLAGS $_DDEBUGFLAGS $_DFLAGS -c -of$TARGET $SOURCES' - env['_DINCFLAGS'] = '$( ${_concat(DINCPREFIX, DPATH, DINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' - env['_DVERFLAGS'] = '$( ${_concat(DVERPREFIX, DVERSIONS, DVERSUFFIX, __env__)} $)' - env['_DDEBUGFLAGS'] = '$( ${_concat(DDEBUGPREFIX, DDEBUG, DDEBUGSUFFIX, __env__)} $)' - env['_DFLAGS'] = '$( ${_concat(DFLAGPREFIX, DFLAGS, DFLAGSUFFIX, __env__)} $)' - - env['DPATH'] = ['#/'] - env['DFLAGS'] = [] - env['DVERSIONS'] = [] - env['DDEBUG'] = [] - - if dc: - # Add the path to the standard library. - # This is merely for the convenience of the dependency scanner. - dmd_path = env.WhereIs(dc) - if dmd_path: - x = dmd_path.rindex(dc) - phobosDir = dmd_path[:x] + '/../src/phobos' - if os.path.isdir(phobosDir): - env.Append(DPATH = [phobosDir]) - - env['DINCPREFIX'] = '-I' - env['DINCSUFFIX'] = '' - env['DVERPREFIX'] = '-version=' - env['DVERSUFFIX'] = '' - env['DDEBUGPREFIX'] = '-debug=' - env['DDEBUGSUFFIX'] = '' - env['DFLAGPREFIX'] = '-' - env['DFLAGSUFFIX'] = '' - env['DFILESUFFIX'] = '.d' - - # Need to use the Digital Mars linker/lib on windows. - # *nix can just use GNU link. - if env['PLATFORM'] == 'win32': - env['DLINK'] = '$DC' - env['DLINKCOM'] = '$DLINK -of$TARGET $SOURCES $DFLAGS $DLINKFLAGS $_DLINKLIBFLAGS' - env['DLIB'] = 'lib' - env['DLIBCOM'] = '$DLIB $_DLIBFLAGS -c $TARGET $SOURCES $_DLINKLIBFLAGS' - - env['_DLINKLIBFLAGS'] = '$( ${_concat(DLIBLINKPREFIX, LIBS, DLIBLINKSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' - env['_DLIBFLAGS'] = '$( ${_concat(DLIBFLAGPREFIX, DLIBFLAGS, DLIBFLAGSUFFIX, __env__)} $)' - env['DLINKFLAGS'] = [] - env['DLIBLINKPREFIX'] = '' - env['DLIBLINKSUFFIX'] = '.lib' - env['DLIBFLAGPREFIX'] = '-' - env['DLIBFLAGSUFFIX'] = '' - env['DLINKFLAGPREFIX'] = '-' - env['DLINKFLAGSUFFIX'] = '' - - SCons.Tool.createStaticLibBuilder(env) - - # Basically, we hijack the link and ar builders with our own. - # these builders check for the presence of D source, and swap out - # the system's defaults for the Digital Mars tools. If there's no D - # source, then we silently return the previous settings. - linkcom = env.get('LINKCOM') - try: - env['SMART_LINKCOM'] = smart_link[linkcom] - except KeyError: - def _smartLink(source, target, env, for_signature, - defaultLinker=linkcom): - if isD(source): - # XXX I'm not sure how to add a $DLINKCOMSTR variable - # so that it works with this _smartLink() logic, - # and I don't have a D compiler/linker to try it out, - # so we'll leave it alone for now. - return '$DLINKCOM' - else: - return defaultLinker - env['SMART_LINKCOM'] = smart_link[linkcom] = _smartLink - - arcom = env.get('ARCOM') - try: - env['SMART_ARCOM'] = smart_lib[arcom] - except KeyError: - def _smartLib(source, target, env, for_signature, - defaultLib=arcom): - if isD(source): - # XXX I'm not sure how to add a $DLIBCOMSTR variable - # so that it works with this _smartLib() logic, and - # I don't have a D compiler/archiver to try it out, - # so we'll leave it alone for now. - return '$DLIBCOM' - else: - return defaultLib - env['SMART_ARCOM'] = smart_lib[arcom] = _smartLib - - # It is worth noting that the final space in these strings is - # absolutely pivotal. SCons sees these as actions and not generators - # if it is not there. (very bad) - env['ARCOM'] = '$SMART_ARCOM ' - env['LINKCOM'] = '$SMART_LINKCOM ' - else: # assuming linux - linkcom = env.get('LINKCOM') - try: - env['SMART_LINKCOM'] = smart_link[linkcom] - except KeyError: - def _smartLink(source, target, env, for_signature, - defaultLinker=linkcom, dc=dc): - if isD(source): - try: - libs = env['LIBS'] - except KeyError: - libs = [] - if dc == 'dmd': - # TODO: This assumes that the dmd executable is in the - # bin directory and that the libraries are in a peer - # directory lib. This true of the Digital Mars - # distribution but . . . - import glob - dHome = env.WhereIs(dc).replace('/dmd' , '/..') - if glob.glob(dHome + '/lib/*phobos2*'): - if 'phobos2' not in libs: - env.Append(LIBPATH = [dHome + '/lib']) - env.Append(LIBS = ['phobos2']) - # TODO: Find out when there will be a - # 64-bit version of D. - env.Append(LINKFLAGS = ['-m32']) - else: - if 'phobos' not in libs: - env.Append(LIBS = ['phobos']) - elif dc is 'gdmd': - env.Append(LIBS = ['gphobos']) - if 'pthread' not in libs: - env.Append(LIBS = ['pthread']) - if 'm' not in libs: - env.Append(LIBS = ['m']) - return defaultLinker - env['SMART_LINKCOM'] = smart_link[linkcom] = _smartLink - - env['LINKCOM'] = '$SMART_LINKCOM ' - -def exists(env): - return env.Detect(['dmd', 'gdmd']) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/dvi.py b/scons/scons-local-2.2.0/SCons/Tool/dvi.py deleted file mode 100644 index 803758cfa..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/dvi.py +++ /dev/null @@ -1,64 +0,0 @@ -"""SCons.Tool.dvi - -Common DVI Builder definition for various other Tool modules that use it. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/dvi.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Builder -import SCons.Tool - -DVIBuilder = None - -def generate(env): - try: - env['BUILDERS']['DVI'] - except KeyError: - global DVIBuilder - - if DVIBuilder is None: - # The suffix is hard-coded to '.dvi', not configurable via a - # construction variable like $DVISUFFIX, because the output - # file name is hard-coded within TeX. - DVIBuilder = SCons.Builder.Builder(action = {}, - source_scanner = SCons.Tool.LaTeXScanner, - suffix = '.dvi', - emitter = {}, - source_ext_match = None) - - env['BUILDERS']['DVI'] = DVIBuilder - -def exists(env): - # This only puts a skeleton Builder in place, so if someone - # references this Tool directly, it's always "available." - return 1 - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/dvipdf.py b/scons/scons-local-2.2.0/SCons/Tool/dvipdf.py deleted file mode 100644 index b931cf56d..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/dvipdf.py +++ /dev/null @@ -1,125 +0,0 @@ -"""SCons.Tool.dvipdf - -Tool-specific initialization for dvipdf. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Tool/dvipdf.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Action -import SCons.Defaults -import SCons.Tool.pdf -import SCons.Tool.tex -import SCons.Util - -_null = SCons.Scanner.LaTeX._null - -def DviPdfPsFunction(XXXDviAction, target = None, source= None, env=None): - """A builder for DVI files that sets the TEXPICTS environment - variable before running dvi2ps or dvipdf.""" - - try: - abspath = source[0].attributes.path - except AttributeError : - abspath = '' - - saved_env = SCons.Scanner.LaTeX.modify_env_var(env, 'TEXPICTS', abspath) - - result = XXXDviAction(target, source, env) - - if saved_env is _null: - try: - del env['ENV']['TEXPICTS'] - except KeyError: - pass # was never set - else: - env['ENV']['TEXPICTS'] = saved_env - - return result - -def DviPdfFunction(target = None, source= None, env=None): - result = DviPdfPsFunction(PDFAction,target,source,env) - return result - -def DviPdfStrFunction(target = None, source= None, env=None): - """A strfunction for dvipdf that returns the appropriate - command string for the no_exec options.""" - if env.GetOption("no_exec"): - result = env.subst('$DVIPDFCOM',0,target,source) - else: - result = '' - return result - -PDFAction = None -DVIPDFAction = None - -def PDFEmitter(target, source, env): - """Strips any .aux or .log files from the input source list. - These are created by the TeX Builder that in all likelihood was - used to generate the .dvi file we're using as input, and we only - care about the .dvi file. - """ - def strip_suffixes(n): - return not SCons.Util.splitext(str(n))[1] in ['.aux', '.log'] - source = list(filter(strip_suffixes, source)) - return (target, source) - -def generate(env): - """Add Builders and construction variables for dvipdf to an Environment.""" - global PDFAction - if PDFAction is None: - PDFAction = SCons.Action.Action('$DVIPDFCOM', '$DVIPDFCOMSTR') - - global DVIPDFAction - if DVIPDFAction is None: - DVIPDFAction = SCons.Action.Action(DviPdfFunction, strfunction = DviPdfStrFunction) - - import pdf - pdf.generate(env) - - bld = env['BUILDERS']['PDF'] - bld.add_action('.dvi', DVIPDFAction) - bld.add_emitter('.dvi', PDFEmitter) - - env['DVIPDF'] = 'dvipdf' - env['DVIPDFFLAGS'] = SCons.Util.CLVar('') - env['DVIPDFCOM'] = 'cd ${TARGET.dir} && $DVIPDF $DVIPDFFLAGS ${SOURCE.file} ${TARGET.file}' - - # Deprecated synonym. - env['PDFCOM'] = ['$DVIPDFCOM'] - -def exists(env): - SCons.Tool.tex.generate_darwin(env) - return env.Detect('dvipdf') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/dvips.py b/scons/scons-local-2.2.0/SCons/Tool/dvips.py deleted file mode 100644 index 8b3ba0f54..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/dvips.py +++ /dev/null @@ -1,95 +0,0 @@ -"""SCons.Tool.dvips - -Tool-specific initialization for dvips. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/dvips.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Action -import SCons.Builder -import SCons.Tool.dvipdf -import SCons.Util - -def DviPsFunction(target = None, source= None, env=None): - result = SCons.Tool.dvipdf.DviPdfPsFunction(PSAction,target,source,env) - return result - -def DviPsStrFunction(target = None, source= None, env=None): - """A strfunction for dvipdf that returns the appropriate - command string for the no_exec options.""" - if env.GetOption("no_exec"): - result = env.subst('$PSCOM',0,target,source) - else: - result = '' - return result - -PSAction = None -DVIPSAction = None -PSBuilder = None - -def generate(env): - """Add Builders and construction variables for dvips to an Environment.""" - global PSAction - if PSAction is None: - PSAction = SCons.Action.Action('$PSCOM', '$PSCOMSTR') - - global DVIPSAction - if DVIPSAction is None: - DVIPSAction = SCons.Action.Action(DviPsFunction, strfunction = DviPsStrFunction) - - global PSBuilder - if PSBuilder is None: - PSBuilder = SCons.Builder.Builder(action = PSAction, - prefix = '$PSPREFIX', - suffix = '$PSSUFFIX', - src_suffix = '.dvi', - src_builder = 'DVI', - single_source=True) - - env['BUILDERS']['PostScript'] = PSBuilder - - env['DVIPS'] = 'dvips' - env['DVIPSFLAGS'] = SCons.Util.CLVar('') - # I'm not quite sure I got the directories and filenames right for variant_dir - # We need to be in the correct directory for the sake of latex \includegraphics eps included files. - env['PSCOM'] = 'cd ${TARGET.dir} && $DVIPS $DVIPSFLAGS -o ${TARGET.file} ${SOURCE.file}' - env['PSPREFIX'] = '' - env['PSSUFFIX'] = '.ps' - -def exists(env): - SCons.Tool.tex.generate_darwin(env) - return env.Detect('dvips') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/f03.py b/scons/scons-local-2.2.0/SCons/Tool/f03.py deleted file mode 100644 index cc8f9d29d..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/f03.py +++ /dev/null @@ -1,63 +0,0 @@ -"""engine.SCons.Tool.f03 - -Tool-specific initialization for the generic Posix f03 Fortran compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/f03.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Defaults -import SCons.Tool -import SCons.Util -import fortran -from SCons.Tool.FortranCommon import add_all_to_env, add_f03_to_env - -compilers = ['f03'] - -def generate(env): - add_all_to_env(env) - add_f03_to_env(env) - - fcomp = env.Detect(compilers) or 'f03' - env['F03'] = fcomp - env['SHF03'] = fcomp - - env['FORTRAN'] = fcomp - env['SHFORTRAN'] = fcomp - - -def exists(env): - return env.Detect(compilers) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/f77.py b/scons/scons-local-2.2.0/SCons/Tool/f77.py deleted file mode 100644 index cba5b0b21..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/f77.py +++ /dev/null @@ -1,62 +0,0 @@ -"""engine.SCons.Tool.f77 - -Tool-specific initialization for the generic Posix f77 Fortran compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/f77.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Defaults -import SCons.Scanner.Fortran -import SCons.Tool -import SCons.Util -from SCons.Tool.FortranCommon import add_all_to_env, add_f77_to_env - -compilers = ['f77'] - -def generate(env): - add_all_to_env(env) - add_f77_to_env(env) - - fcomp = env.Detect(compilers) or 'f77' - env['F77'] = fcomp - env['SHF77'] = fcomp - - env['FORTRAN'] = fcomp - env['SHFORTRAN'] = fcomp - -def exists(env): - return env.Detect(compilers) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/f90.py b/scons/scons-local-2.2.0/SCons/Tool/f90.py deleted file mode 100644 index 1df001405..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/f90.py +++ /dev/null @@ -1,62 +0,0 @@ -"""engine.SCons.Tool.f90 - -Tool-specific initialization for the generic Posix f90 Fortran compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/f90.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Defaults -import SCons.Scanner.Fortran -import SCons.Tool -import SCons.Util -from SCons.Tool.FortranCommon import add_all_to_env, add_f90_to_env - -compilers = ['f90'] - -def generate(env): - add_all_to_env(env) - add_f90_to_env(env) - - fc = env.Detect(compilers) or 'f90' - env['F90'] = fc - env['SHF90'] = fc - - env['FORTRAN'] = fc - env['SHFORTRAN'] = fc - -def exists(env): - return env.Detect(compilers) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/f95.py b/scons/scons-local-2.2.0/SCons/Tool/f95.py deleted file mode 100644 index b32530905..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/f95.py +++ /dev/null @@ -1,63 +0,0 @@ -"""engine.SCons.Tool.f95 - -Tool-specific initialization for the generic Posix f95 Fortran compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/f95.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Defaults -import SCons.Tool -import SCons.Util -import fortran -from SCons.Tool.FortranCommon import add_all_to_env, add_f95_to_env - -compilers = ['f95'] - -def generate(env): - add_all_to_env(env) - add_f95_to_env(env) - - fcomp = env.Detect(compilers) or 'f95' - env['F95'] = fcomp - env['SHF95'] = fcomp - - env['FORTRAN'] = fcomp - env['SHFORTRAN'] = fcomp - - -def exists(env): - return env.Detect(compilers) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/filesystem.py b/scons/scons-local-2.2.0/SCons/Tool/filesystem.py deleted file mode 100644 index 2ac49548f..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/filesystem.py +++ /dev/null @@ -1,98 +0,0 @@ -"""SCons.Tool.filesystem - -Tool-specific initialization for the filesystem tools. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/filesystem.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons -from SCons.Tool.install import copyFunc - -copyToBuilder, copyAsBuilder = None, None - -def copyto_emitter(target, source, env): - """ changes the path of the source to be under the target (which - are assumed to be directories. - """ - n_target = [] - - for t in target: - n_target = n_target + [t.File( str( s ) ) for s in source] - - return (n_target, source) - -def copy_action_func(target, source, env): - assert( len(target) == len(source) ), "\ntarget: %s\nsource: %s" %(list(map(str, target)),list(map(str, source))) - - for t, s in zip(target, source): - if copyFunc(t.get_path(), s.get_path(), env): - return 1 - - return 0 - -def copy_action_str(target, source, env): - return env.subst_target_source(env['COPYSTR'], 0, target, source) - -copy_action = SCons.Action.Action( copy_action_func, copy_action_str ) - -def generate(env): - try: - env['BUILDERS']['CopyTo'] - env['BUILDERS']['CopyAs'] - except KeyError, e: - global copyToBuilder - if copyToBuilder is None: - copyToBuilder = SCons.Builder.Builder( - action = copy_action, - target_factory = env.fs.Dir, - source_factory = env.fs.Entry, - multi = 1, - emitter = [ copyto_emitter, ] ) - - global copyAsBuilder - if copyAsBuilder is None: - copyAsBuilder = SCons.Builder.Builder( - action = copy_action, - target_factory = env.fs.Entry, - source_factory = env.fs.Entry ) - - env['BUILDERS']['CopyTo'] = copyToBuilder - env['BUILDERS']['CopyAs'] = copyAsBuilder - - env['COPYSTR'] = 'Copy file(s): "$SOURCES" to "$TARGETS"' - -def exists(env): - return 1 - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/fortran.py b/scons/scons-local-2.2.0/SCons/Tool/fortran.py deleted file mode 100644 index 3da748a94..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/fortran.py +++ /dev/null @@ -1,62 +0,0 @@ -"""SCons.Tool.fortran - -Tool-specific initialization for a generic Posix f77/f90 Fortran compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/fortran.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import re - -import SCons.Action -import SCons.Defaults -import SCons.Scanner.Fortran -import SCons.Tool -import SCons.Util -from SCons.Tool.FortranCommon import add_all_to_env, add_fortran_to_env - -compilers = ['f95', 'f90', 'f77'] - -def generate(env): - add_all_to_env(env) - add_fortran_to_env(env) - - fc = env.Detect(compilers) or 'f77' - env['SHFORTRAN'] = fc - env['FORTRAN'] = fc - -def exists(env): - return env.Detect(compilers) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/g++.py b/scons/scons-local-2.2.0/SCons/Tool/g++.py deleted file mode 100644 index 484344c3c..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/g++.py +++ /dev/null @@ -1,90 +0,0 @@ -"""SCons.Tool.g++ - -Tool-specific initialization for g++. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/g++.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os.path -import re -import subprocess - -import SCons.Tool -import SCons.Util - -cplusplus = __import__('c++', globals(), locals(), []) - -compilers = ['g++'] - -def generate(env): - """Add Builders and construction variables for g++ to an Environment.""" - static_obj, shared_obj = SCons.Tool.createObjBuilders(env) - - cplusplus.generate(env) - - env['CXX'] = env.Detect(compilers) - - # platform specific settings - if env['PLATFORM'] == 'aix': - env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -mminimal-toc') - env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 - env['SHOBJSUFFIX'] = '$OBJSUFFIX' - elif env['PLATFORM'] == 'hpux': - env['SHOBJSUFFIX'] = '.pic.o' - elif env['PLATFORM'] == 'sunos': - env['SHOBJSUFFIX'] = '.pic.o' - # determine compiler version - if env['CXX']: - #pipe = SCons.Action._subproc(env, [env['CXX'], '-dumpversion'], - pipe = SCons.Action._subproc(env, [env['CXX'], '--version'], - stdin = 'devnull', - stderr = 'devnull', - stdout = subprocess.PIPE) - if pipe.wait() != 0: return - # -dumpversion was added in GCC 3.0. As long as we're supporting - # GCC versions older than that, we should use --version and a - # regular expression. - #line = pipe.stdout.read().strip() - #if line: - # env['CXXVERSION'] = line - line = pipe.stdout.readline() - match = re.search(r'[0-9]+(\.[0-9]+)+', line) - if match: - env['CXXVERSION'] = match.group(0) - -def exists(env): - return env.Detect(compilers) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/g77.py b/scons/scons-local-2.2.0/SCons/Tool/g77.py deleted file mode 100644 index 97c2ef184..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/g77.py +++ /dev/null @@ -1,73 +0,0 @@ -"""engine.SCons.Tool.g77 - -Tool-specific initialization for g77. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/g77.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Util -from SCons.Tool.FortranCommon import add_all_to_env, add_f77_to_env - -compilers = ['g77', 'f77'] - -def generate(env): - """Add Builders and construction variables for g77 to an Environment.""" - add_all_to_env(env) - add_f77_to_env(env) - - fcomp = env.Detect(compilers) or 'g77' - if env['PLATFORM'] in ['cygwin', 'win32']: - env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS') - env['SHF77FLAGS'] = SCons.Util.CLVar('$F77FLAGS') - else: - env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -fPIC') - env['SHF77FLAGS'] = SCons.Util.CLVar('$F77FLAGS -fPIC') - - env['FORTRAN'] = fcomp - env['SHFORTRAN'] = '$FORTRAN' - - env['F77'] = fcomp - env['SHF77'] = '$F77' - - env['INCFORTRANPREFIX'] = "-I" - env['INCFORTRANSUFFIX'] = "" - - env['INCF77PREFIX'] = "-I" - env['INCF77SUFFIX'] = "" - -def exists(env): - return env.Detect(compilers) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/gas.py b/scons/scons-local-2.2.0/SCons/Tool/gas.py deleted file mode 100644 index 89aa2e344..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/gas.py +++ /dev/null @@ -1,53 +0,0 @@ -"""SCons.Tool.gas - -Tool-specific initialization for as, the Gnu assembler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/gas.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -as_module = __import__('as', globals(), locals(), []) - -assemblers = ['as', 'gas'] - -def generate(env): - """Add Builders and construction variables for as to an Environment.""" - as_module.generate(env) - - env['AS'] = env.Detect(assemblers) or 'as' - -def exists(env): - return env.Detect(assemblers) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/gcc.py b/scons/scons-local-2.2.0/SCons/Tool/gcc.py deleted file mode 100644 index 814e1dec2..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/gcc.py +++ /dev/null @@ -1,80 +0,0 @@ -"""SCons.Tool.gcc - -Tool-specific initialization for gcc. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/gcc.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import cc -import os -import re -import subprocess - -import SCons.Util - -compilers = ['gcc', 'cc'] - -def generate(env): - """Add Builders and construction variables for gcc to an Environment.""" - cc.generate(env) - - env['CC'] = env.Detect(compilers) or 'gcc' - if env['PLATFORM'] in ['cygwin', 'win32']: - env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') - else: - env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS -fPIC') - # determine compiler version - if env['CC']: - #pipe = SCons.Action._subproc(env, [env['CC'], '-dumpversion'], - pipe = SCons.Action._subproc(env, [env['CC'], '--version'], - stdin = 'devnull', - stderr = 'devnull', - stdout = subprocess.PIPE) - if pipe.wait() != 0: return - # -dumpversion was added in GCC 3.0. As long as we're supporting - # GCC versions older than that, we should use --version and a - # regular expression. - #line = pipe.stdout.read().strip() - #if line: - # env['CCVERSION'] = line - line = pipe.stdout.readline() - match = re.search(r'[0-9]+(\.[0-9]+)+', line) - if match: - env['CCVERSION'] = match.group(0) - -def exists(env): - return env.Detect(compilers) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/gettext.py b/scons/scons-local-2.2.0/SCons/Tool/gettext.py deleted file mode 100644 index 9f2c70715..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/gettext.py +++ /dev/null @@ -1,45 +0,0 @@ -"""gettext tool -""" - - -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Tool/gettext.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -############################################################################# -def generate(env,**kw): - import SCons.Tool - from SCons.Tool.GettextCommon \ - import _translate, tool_list - for t in tool_list(env['PLATFORM'], env): - env.Tool(t) - env.AddMethod(_translate, 'Translate') -############################################################################# - -############################################################################# -def exists(env): - from SCons.Tool.GettextCommon \ - import _xgettext_exists, _msginit_exists, \ - _msgmerge_exists, _msgfmt_exists - return _xgettext_exists(env) and _msginit_exists(env) \ - and _msgmerge_exists(env) and _msgfmt_exists(env) -############################################################################# diff --git a/scons/scons-local-2.2.0/SCons/Tool/gfortran.py b/scons/scons-local-2.2.0/SCons/Tool/gfortran.py deleted file mode 100644 index 2f9bddc64..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/gfortran.py +++ /dev/null @@ -1,64 +0,0 @@ -"""SCons.Tool.gfortran - -Tool-specific initialization for gfortran, the GNU Fortran 95/Fortran -2003 compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/gfortran.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Util - -import fortran - -def generate(env): - """Add Builders and construction variables for gfortran to an - Environment.""" - fortran.generate(env) - - for dialect in ['F77', 'F90', 'FORTRAN', 'F95', 'F03']: - env['%s' % dialect] = 'gfortran' - env['SH%s' % dialect] = '$%s' % dialect - if env['PLATFORM'] in ['cygwin', 'win32']: - env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS' % dialect) - else: - env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS -fPIC' % dialect) - - env['INC%sPREFIX' % dialect] = "-I" - env['INC%sSUFFIX' % dialect] = "" - -def exists(env): - return env.Detect('gfortran') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/gnulink.py b/scons/scons-local-2.2.0/SCons/Tool/gnulink.py deleted file mode 100644 index ee9f584e9..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/gnulink.py +++ /dev/null @@ -1,62 +0,0 @@ -"""SCons.Tool.gnulink - -Tool-specific initialization for the gnu linker. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/gnulink.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Util - -import link - -linkers = ['g++', 'gcc'] - -def generate(env): - """Add Builders and construction variables for gnulink to an Environment.""" - link.generate(env) - - if env['PLATFORM'] == 'hpux': - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared -fPIC') - - # __RPATH is set to $_RPATH in the platform specification if that - # platform supports it. - env['RPATHPREFIX'] = '-Wl,-rpath=' - env['RPATHSUFFIX'] = '' - env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}' - -def exists(env): - return env.Detect(linkers) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/gs.py b/scons/scons-local-2.2.0/SCons/Tool/gs.py deleted file mode 100644 index 12e71d282..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/gs.py +++ /dev/null @@ -1,81 +0,0 @@ -"""SCons.Tool.gs - -Tool-specific initialization for Ghostscript. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/gs.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Action -import SCons.Platform -import SCons.Util - -# Ghostscript goes by different names on different platforms... -platform = SCons.Platform.platform_default() - -if platform == 'os2': - gs = 'gsos2' -elif platform == 'win32': - gs = 'gswin32c' -else: - gs = 'gs' - -GhostscriptAction = None - -def generate(env): - """Add Builders and construction variables for Ghostscript to an - Environment.""" - - global GhostscriptAction - if GhostscriptAction is None: - GhostscriptAction = SCons.Action.Action('$GSCOM', '$GSCOMSTR') - - import pdf - pdf.generate(env) - - bld = env['BUILDERS']['PDF'] - bld.add_action('.ps', GhostscriptAction) - - env['GS'] = gs - env['GSFLAGS'] = SCons.Util.CLVar('-dNOPAUSE -dBATCH -sDEVICE=pdfwrite') - env['GSCOM'] = '$GS $GSFLAGS -sOutputFile=$TARGET $SOURCES' - - -def exists(env): - if 'PS2PDF' in env: - return env.Detect(env['PS2PDF']) - else: - return env.Detect(gs) or SCons.Util.WhereIs(gs) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/hpc++.py b/scons/scons-local-2.2.0/SCons/Tool/hpc++.py deleted file mode 100644 index 5f75e1807..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/hpc++.py +++ /dev/null @@ -1,84 +0,0 @@ -"""SCons.Tool.hpc++ - -Tool-specific initialization for c++ on HP/UX. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/hpc++.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os.path - -import SCons.Util - -cplusplus = __import__('c++', globals(), locals(), []) - -acc = None - -# search for the acc compiler and linker front end - -try: - dirs = os.listdir('/opt') -except (IOError, OSError): - # Not being able to read the directory because it doesn't exist - # (IOError) or isn't readable (OSError) is okay. - dirs = [] - -for dir in dirs: - cc = '/opt/' + dir + '/bin/aCC' - if os.path.exists(cc): - acc = cc - break - - -def generate(env): - """Add Builders and construction variables for g++ to an Environment.""" - cplusplus.generate(env) - - if acc: - env['CXX'] = acc or 'aCC' - env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS +Z') - # determine version of aCC - line = os.popen(acc + ' -V 2>&1').readline().rstrip() - if line.find('aCC: HP ANSI C++') == 0: - env['CXXVERSION'] = line.split()[-1] - - if env['PLATFORM'] == 'cygwin': - env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') - else: - env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS +Z') - -def exists(env): - return acc - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/hpcc.py b/scons/scons-local-2.2.0/SCons/Tool/hpcc.py deleted file mode 100644 index 29586f623..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/hpcc.py +++ /dev/null @@ -1,53 +0,0 @@ -"""SCons.Tool.hpcc - -Tool-specific initialization for HP aCC and cc. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/hpcc.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Util - -import cc - -def generate(env): - """Add Builders and construction variables for aCC & cc to an Environment.""" - cc.generate(env) - - env['CXX'] = 'aCC' - env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS +Z') - -def exists(env): - return env.Detect('aCC') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/hplink.py b/scons/scons-local-2.2.0/SCons/Tool/hplink.py deleted file mode 100644 index d979545e6..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/hplink.py +++ /dev/null @@ -1,77 +0,0 @@ -"""SCons.Tool.hplink - -Tool-specific initialization for the HP linker. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/hplink.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os -import os.path - -import SCons.Util - -import link - -ccLinker = None - -# search for the acc compiler and linker front end - -try: - dirs = os.listdir('/opt') -except (IOError, OSError): - # Not being able to read the directory because it doesn't exist - # (IOError) or isn't readable (OSError) is okay. - dirs = [] - -for dir in dirs: - linker = '/opt/' + dir + '/bin/aCC' - if os.path.exists(linker): - ccLinker = linker - break - -def generate(env): - """ - Add Builders and construction variables for Visual Age linker to - an Environment. - """ - link.generate(env) - - env['LINKFLAGS'] = SCons.Util.CLVar('-Wl,+s -Wl,+vnocompatwarnings') - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -b') - env['SHLIBSUFFIX'] = '.sl' - -def exists(env): - return ccLinker - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/icc.py b/scons/scons-local-2.2.0/SCons/Tool/icc.py deleted file mode 100644 index 1f7f02847..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/icc.py +++ /dev/null @@ -1,59 +0,0 @@ -"""engine.SCons.Tool.icc - -Tool-specific initialization for the OS/2 icc compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/icc.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import cc - -def generate(env): - """Add Builders and construction variables for the OS/2 to an Environment.""" - cc.generate(env) - - env['CC'] = 'icc' - env['CCCOM'] = '$CC $CFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET' - env['CXXCOM'] = '$CXX $CXXFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET' - env['CPPDEFPREFIX'] = '/D' - env['CPPDEFSUFFIX'] = '' - env['INCPREFIX'] = '/I' - env['INCSUFFIX'] = '' - env['CFILESUFFIX'] = '.c' - env['CXXFILESUFFIX'] = '.cc' - -def exists(env): - return env.Detect('icc') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/icl.py b/scons/scons-local-2.2.0/SCons/Tool/icl.py deleted file mode 100644 index e3ee4ea5d..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/icl.py +++ /dev/null @@ -1,52 +0,0 @@ -"""engine.SCons.Tool.icl - -Tool-specific initialization for the Intel C/C++ compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/icl.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Tool.intelc - -# This has been completely superceded by intelc.py, which can -# handle both Windows and Linux versions. - -def generate(*args, **kw): - """Add Builders and construction variables for icl to an Environment.""" - return SCons.Tool.intelc.generate(*args, **kw) - -def exists(*args, **kw): - return SCons.Tool.intelc.exists(*args, **kw) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/ifl.py b/scons/scons-local-2.2.0/SCons/Tool/ifl.py deleted file mode 100644 index 6ad250abb..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/ifl.py +++ /dev/null @@ -1,72 +0,0 @@ -"""SCons.Tool.ifl - -Tool-specific initialization for the Intel Fortran compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/ifl.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Defaults -from SCons.Scanner.Fortran import FortranScan -from FortranCommon import add_all_to_env - -def generate(env): - """Add Builders and construction variables for ifl to an Environment.""" - fscan = FortranScan("FORTRANPATH") - SCons.Tool.SourceFileScanner.add_scanner('.i', fscan) - SCons.Tool.SourceFileScanner.add_scanner('.i90', fscan) - - if 'FORTRANFILESUFFIXES' not in env: - env['FORTRANFILESUFFIXES'] = ['.i'] - else: - env['FORTRANFILESUFFIXES'].append('.i') - - if 'F90FILESUFFIXES' not in env: - env['F90FILESUFFIXES'] = ['.i90'] - else: - env['F90FILESUFFIXES'].append('.i90') - - add_all_to_env(env) - - env['FORTRAN'] = 'ifl' - env['SHFORTRAN'] = '$FORTRAN' - env['FORTRANCOM'] = '$FORTRAN $FORTRANFLAGS $_FORTRANINCFLAGS /c $SOURCES /Fo$TARGET' - env['FORTRANPPCOM'] = '$FORTRAN $FORTRANFLAGS $CPPFLAGS $_CPPDEFFLAGS $_FORTRANINCFLAGS /c $SOURCES /Fo$TARGET' - env['SHFORTRANCOM'] = '$SHFORTRAN $SHFORTRANFLAGS $_FORTRANINCFLAGS /c $SOURCES /Fo$TARGET' - env['SHFORTRANPPCOM'] = '$SHFORTRAN $SHFORTRANFLAGS $CPPFLAGS $_CPPDEFFLAGS $_FORTRANINCFLAGS /c $SOURCES /Fo$TARGET' - -def exists(env): - return env.Detect('ifl') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/ifort.py b/scons/scons-local-2.2.0/SCons/Tool/ifort.py deleted file mode 100644 index fde2e864f..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/ifort.py +++ /dev/null @@ -1,88 +0,0 @@ -"""SCons.Tool.ifort - -Tool-specific initialization for newer versions of the Intel Fortran Compiler -for Linux/Windows (and possibly Mac OS X). - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/ifort.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Defaults -from SCons.Scanner.Fortran import FortranScan -from FortranCommon import add_all_to_env - -def generate(env): - """Add Builders and construction variables for ifort to an Environment.""" - # ifort supports Fortran 90 and Fortran 95 - # Additionally, ifort recognizes more file extensions. - fscan = FortranScan("FORTRANPATH") - SCons.Tool.SourceFileScanner.add_scanner('.i', fscan) - SCons.Tool.SourceFileScanner.add_scanner('.i90', fscan) - - if 'FORTRANFILESUFFIXES' not in env: - env['FORTRANFILESUFFIXES'] = ['.i'] - else: - env['FORTRANFILESUFFIXES'].append('.i') - - if 'F90FILESUFFIXES' not in env: - env['F90FILESUFFIXES'] = ['.i90'] - else: - env['F90FILESUFFIXES'].append('.i90') - - add_all_to_env(env) - - fc = 'ifort' - - for dialect in ['F77', 'F90', 'FORTRAN', 'F95']: - env['%s' % dialect] = fc - env['SH%s' % dialect] = '$%s' % dialect - if env['PLATFORM'] == 'posix': - env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS -fPIC' % dialect) - - if env['PLATFORM'] == 'win32': - # On Windows, the ifort compiler specifies the object on the - # command line with -object:, not -o. Massage the necessary - # command-line construction variables. - for dialect in ['F77', 'F90', 'FORTRAN', 'F95']: - for var in ['%sCOM' % dialect, '%sPPCOM' % dialect, - 'SH%sCOM' % dialect, 'SH%sPPCOM' % dialect]: - env[var] = env[var].replace('-o $TARGET', '-object:$TARGET') - env['FORTRANMODDIRPREFIX'] = "/module:" - else: - env['FORTRANMODDIRPREFIX'] = "-module " - -def exists(env): - return env.Detect('ifort') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/ilink.py b/scons/scons-local-2.2.0/SCons/Tool/ilink.py deleted file mode 100644 index 4f37906b9..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/ilink.py +++ /dev/null @@ -1,59 +0,0 @@ -"""SCons.Tool.ilink - -Tool-specific initialization for the OS/2 ilink linker. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/ilink.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Defaults -import SCons.Tool -import SCons.Util - -def generate(env): - """Add Builders and construction variables for ilink to an Environment.""" - SCons.Tool.createProgBuilder(env) - - env['LINK'] = 'ilink' - env['LINKFLAGS'] = SCons.Util.CLVar('') - env['LINKCOM'] = '$LINK $LINKFLAGS /O:$TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' - env['LIBDIRPREFIX']='/LIBPATH:' - env['LIBDIRSUFFIX']='' - env['LIBLINKPREFIX']='' - env['LIBLINKSUFFIX']='$LIBSUFFIX' - -def exists(env): - return env.Detect('ilink') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/ilink32.py b/scons/scons-local-2.2.0/SCons/Tool/ilink32.py deleted file mode 100644 index 399501b6b..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/ilink32.py +++ /dev/null @@ -1,60 +0,0 @@ -"""SCons.Tool.ilink32 - -XXX - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/ilink32.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Tool -import SCons.Tool.bcc32 -import SCons.Util - -def generate(env): - """Add Builders and construction variables for Borland ilink to an - Environment.""" - SCons.Tool.createSharedLibBuilder(env) - SCons.Tool.createProgBuilder(env) - - env['LINK'] = '$CC' - env['LINKFLAGS'] = SCons.Util.CLVar('') - env['LINKCOM'] = '$LINK -q $LINKFLAGS -e$TARGET $SOURCES $LIBS' - env['LIBDIRPREFIX']='' - env['LIBDIRSUFFIX']='' - env['LIBLINKPREFIX']='' - env['LIBLINKSUFFIX']='$LIBSUFFIX' - - -def exists(env): - # Uses bcc32 to do linking as it generally knows where the standard - # LIBS are and set up the linking correctly - return SCons.Tool.bcc32.findIt('bcc32', env) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/install.py b/scons/scons-local-2.2.0/SCons/Tool/install.py deleted file mode 100644 index 8b0673b06..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/install.py +++ /dev/null @@ -1,283 +0,0 @@ -"""SCons.Tool.install - -Tool-specific initialization for the install tool. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/install.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os -import shutil -import stat - -import SCons.Action -from SCons.Util import make_path_relative - -# -# We keep track of *all* installed files. -_INSTALLED_FILES = [] -_UNIQUE_INSTALLED_FILES = None - -class CopytreeError(EnvironmentError): - pass - -# This is a patched version of shutil.copytree from python 2.5. It -# doesn't fail if the dir exists, which regular copytree does -# (annoyingly). Note the XXX comment in the docstring. -def scons_copytree(src, dst, symlinks=False): - """Recursively copy a directory tree using copy2(). - - The destination directory must not already exist. - If exception(s) occur, an CopytreeError is raised with a list of reasons. - - If the optional symlinks flag is true, symbolic links in the - source tree result in symbolic links in the destination tree; if - it is false, the contents of the files pointed to by symbolic - links are copied. - - XXX Consider this example code rather than the ultimate tool. - - """ - names = os.listdir(src) - # garyo@genarts.com fix: check for dir before making dirs. - if not os.path.exists(dst): - os.makedirs(dst) - errors = [] - for name in names: - srcname = os.path.join(src, name) - dstname = os.path.join(dst, name) - try: - if symlinks and os.path.islink(srcname): - linkto = os.readlink(srcname) - os.symlink(linkto, dstname) - elif os.path.isdir(srcname): - scons_copytree(srcname, dstname, symlinks) - else: - shutil.copy2(srcname, dstname) - # XXX What about devices, sockets etc.? - except (IOError, os.error), why: - errors.append((srcname, dstname, str(why))) - # catch the CopytreeError from the recursive copytree so that we can - # continue with other files - except CopytreeError, err: - errors.extend(err.args[0]) - try: - shutil.copystat(src, dst) - except WindowsError: - # can't copy file access times on Windows - pass - except OSError, why: - errors.extend((src, dst, str(why))) - if errors: - raise CopytreeError, errors - - -# -# Functions doing the actual work of the Install Builder. -# -def copyFunc(dest, source, env): - """Install a source file or directory into a destination by copying, - (including copying permission/mode bits).""" - - if os.path.isdir(source): - if os.path.exists(dest): - if not os.path.isdir(dest): - raise SCons.Errors.UserError("cannot overwrite non-directory `%s' with a directory `%s'" % (str(dest), str(source))) - else: - parent = os.path.split(dest)[0] - if not os.path.exists(parent): - os.makedirs(parent) - scons_copytree(source, dest) - else: - shutil.copy2(source, dest) - st = os.stat(source) - os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) - - return 0 - -def installFunc(target, source, env): - """Install a source file into a target using the function specified - as the INSTALL construction variable.""" - try: - install = env['INSTALL'] - except KeyError: - raise SCons.Errors.UserError('Missing INSTALL construction variable.') - - assert len(target)==len(source), \ - "Installing source %s into target %s: target and source lists must have same length."%(list(map(str, source)), list(map(str, target))) - for t,s in zip(target,source): - if install(t.get_path(),s.get_path(),env): - return 1 - - return 0 - -def stringFunc(target, source, env): - installstr = env.get('INSTALLSTR') - if installstr: - return env.subst_target_source(installstr, 0, target, source) - target = str(target[0]) - source = str(source[0]) - if os.path.isdir(source): - type = 'directory' - else: - type = 'file' - return 'Install %s: "%s" as "%s"' % (type, source, target) - -# -# Emitter functions -# -def add_targets_to_INSTALLED_FILES(target, source, env): - """ an emitter that adds all target files to the list stored in the - _INSTALLED_FILES global variable. This way all installed files of one - scons call will be collected. - """ - global _INSTALLED_FILES, _UNIQUE_INSTALLED_FILES - _INSTALLED_FILES.extend(target) - _UNIQUE_INSTALLED_FILES = None - return (target, source) - -class DESTDIR_factory(object): - """ a node factory, where all files will be relative to the dir supplied - in the constructor. - """ - def __init__(self, env, dir): - self.env = env - self.dir = env.arg2nodes( dir, env.fs.Dir )[0] - - def Entry(self, name): - name = make_path_relative(name) - return self.dir.Entry(name) - - def Dir(self, name): - name = make_path_relative(name) - return self.dir.Dir(name) - -# -# The Builder Definition -# -install_action = SCons.Action.Action(installFunc, stringFunc) -installas_action = SCons.Action.Action(installFunc, stringFunc) - -BaseInstallBuilder = None - -def InstallBuilderWrapper(env, target=None, source=None, dir=None, **kw): - if target and dir: - import SCons.Errors - raise SCons.Errors.UserError("Both target and dir defined for Install(), only one may be defined.") - if not dir: - dir=target - - import SCons.Script - install_sandbox = SCons.Script.GetOption('install_sandbox') - if install_sandbox: - target_factory = DESTDIR_factory(env, install_sandbox) - else: - target_factory = env.fs - - try: - dnodes = env.arg2nodes(dir, target_factory.Dir) - except TypeError: - raise SCons.Errors.UserError("Target `%s' of Install() is a file, but should be a directory. Perhaps you have the Install() arguments backwards?" % str(dir)) - sources = env.arg2nodes(source, env.fs.Entry) - tgt = [] - for dnode in dnodes: - for src in sources: - # Prepend './' so the lookup doesn't interpret an initial - # '#' on the file name portion as meaning the Node should - # be relative to the top-level SConstruct directory. - target = env.fs.Entry('.'+os.sep+src.name, dnode) - #tgt.extend(BaseInstallBuilder(env, target, src, **kw)) - tgt.extend(BaseInstallBuilder(env, target, src, **kw)) - return tgt - -def InstallAsBuilderWrapper(env, target=None, source=None, **kw): - result = [] - for src, tgt in map(lambda x, y: (x, y), source, target): - #result.extend(BaseInstallBuilder(env, tgt, src, **kw)) - result.extend(BaseInstallBuilder(env, tgt, src, **kw)) - return result - -added = None - -def generate(env): - - from SCons.Script import AddOption, GetOption - global added - if not added: - added = 1 - AddOption('--install-sandbox', - dest='install_sandbox', - type="string", - action="store", - help='A directory under which all installed files will be placed.') - - global BaseInstallBuilder - if BaseInstallBuilder is None: - install_sandbox = GetOption('install_sandbox') - if install_sandbox: - target_factory = DESTDIR_factory(env, install_sandbox) - else: - target_factory = env.fs - - BaseInstallBuilder = SCons.Builder.Builder( - action = install_action, - target_factory = target_factory.Entry, - source_factory = env.fs.Entry, - multi = 1, - emitter = [ add_targets_to_INSTALLED_FILES, ], - name = 'InstallBuilder') - - env['BUILDERS']['_InternalInstall'] = InstallBuilderWrapper - env['BUILDERS']['_InternalInstallAs'] = InstallAsBuilderWrapper - - # We'd like to initialize this doing something like the following, - # but there isn't yet support for a ${SOURCE.type} expansion that - # will print "file" or "directory" depending on what's being - # installed. For now we punt by not initializing it, and letting - # the stringFunc() that we put in the action fall back to the - # hand-crafted default string if it's not set. - # - #try: - # env['INSTALLSTR'] - #except KeyError: - # env['INSTALLSTR'] = 'Install ${SOURCE.type}: "$SOURCES" as "$TARGETS"' - - try: - env['INSTALL'] - except KeyError: - env['INSTALL'] = copyFunc - -def exists(env): - return 1 - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/intelc.py b/scons/scons-local-2.2.0/SCons/Tool/intelc.py deleted file mode 100644 index 92529afdc..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/intelc.py +++ /dev/null @@ -1,522 +0,0 @@ -"""SCons.Tool.icl - -Tool-specific initialization for the Intel C/C++ compiler. -Supports Linux and Windows compilers, v7 and up. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -from __future__ import division - -__revision__ = "src/engine/SCons/Tool/intelc.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import math, sys, os.path, glob, string, re - -is_windows = sys.platform == 'win32' -is_win64 = is_windows and (os.environ['PROCESSOR_ARCHITECTURE'] == 'AMD64' or - ('PROCESSOR_ARCHITEW6432' in os.environ and - os.environ['PROCESSOR_ARCHITEW6432'] == 'AMD64')) -is_linux = sys.platform.startswith('linux') -is_mac = sys.platform == 'darwin' - -if is_windows: - import SCons.Tool.msvc -elif is_linux: - import SCons.Tool.gcc -elif is_mac: - import SCons.Tool.gcc -import SCons.Util -import SCons.Warnings - -# Exceptions for this tool -class IntelCError(SCons.Errors.InternalError): - pass -class MissingRegistryError(IntelCError): # missing registry entry - pass -class MissingDirError(IntelCError): # dir not found - pass -class NoRegistryModuleError(IntelCError): # can't read registry at all - pass - -def uniquify(s): - """Return a sequence containing only one copy of each unique element from input sequence s. - Does not preserve order. - Input sequence must be hashable (i.e. must be usable as a dictionary key).""" - u = {} - for x in s: - u[x] = 1 - return list(u.keys()) - -def linux_ver_normalize(vstr): - """Normalize a Linux compiler version number. - Intel changed from "80" to "9.0" in 2005, so we assume if the number - is greater than 60 it's an old-style number and otherwise new-style. - Always returns an old-style float like 80 or 90 for compatibility with Windows. - Shades of Y2K!""" - # Check for version number like 9.1.026: return 91.026 - m = re.match(r'([0-9]+)\.([0-9]+)\.([0-9]+)', vstr) - if m: - vmaj,vmin,build = m.groups() - return float(vmaj) * 10. + float(vmin) + float(build) / 1000.; - else: - f = float(vstr) - if is_windows: - return f - else: - if f < 60: return f * 10.0 - else: return f - -def check_abi(abi): - """Check for valid ABI (application binary interface) name, - and map into canonical one""" - if not abi: - return None - abi = abi.lower() - # valid_abis maps input name to canonical name - if is_windows: - valid_abis = {'ia32' : 'ia32', - 'x86' : 'ia32', - 'ia64' : 'ia64', - 'em64t' : 'em64t', - 'amd64' : 'em64t'} - if is_linux: - valid_abis = {'ia32' : 'ia32', - 'x86' : 'ia32', - 'x86_64' : 'x86_64', - 'em64t' : 'x86_64', - 'amd64' : 'x86_64'} - if is_mac: - valid_abis = {'ia32' : 'ia32', - 'x86' : 'ia32', - 'x86_64' : 'x86_64', - 'em64t' : 'x86_64'} - try: - abi = valid_abis[abi] - except KeyError: - raise SCons.Errors.UserError("Intel compiler: Invalid ABI %s, valid values are %s"% \ - (abi, list(valid_abis.keys()))) - return abi - -def vercmp(a, b): - """Compare strings as floats, - but Intel changed Linux naming convention at 9.0""" - return cmp(linux_ver_normalize(b), linux_ver_normalize(a)) - -def get_version_from_list(v, vlist): - """See if we can match v (string) in vlist (list of strings) - Linux has to match in a fuzzy way.""" - if is_windows: - # Simple case, just find it in the list - if v in vlist: return v - else: return None - else: - # Fuzzy match: normalize version number first, but still return - # original non-normalized form. - fuzz = 0.001 - for vi in vlist: - if math.fabs(linux_ver_normalize(vi) - linux_ver_normalize(v)) < fuzz: - return vi - # Not found - return None - -def get_intel_registry_value(valuename, version=None, abi=None): - """ - Return a value from the Intel compiler registry tree. (Windows only) - """ - # Open the key: - if is_win64: - K = 'Software\\Wow6432Node\\Intel\\Compilers\\C++\\' + version + '\\'+abi.upper() - else: - K = 'Software\\Intel\\Compilers\\C++\\' + version + '\\'+abi.upper() - try: - k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, K) - except SCons.Util.RegError: - raise MissingRegistryError("%s was not found in the registry, for Intel compiler version %s, abi='%s'"%(K, version,abi)) - - # Get the value: - try: - v = SCons.Util.RegQueryValueEx(k, valuename)[0] - return v # or v.encode('iso-8859-1', 'replace') to remove unicode? - except SCons.Util.RegError: - raise MissingRegistryError("%s\\%s was not found in the registry."%(K, valuename)) - - -def get_all_compiler_versions(): - """Returns a sorted list of strings, like "70" or "80" or "9.0" - with most recent compiler version first. - """ - versions=[] - if is_windows: - if is_win64: - keyname = 'Software\\WoW6432Node\\Intel\\Compilers\\C++' - else: - keyname = 'Software\\Intel\\Compilers\\C++' - try: - k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, - keyname) - except WindowsError: - return [] - i = 0 - versions = [] - try: - while i < 100: - subkey = SCons.Util.RegEnumKey(k, i) # raises EnvironmentError - # Check that this refers to an existing dir. - # This is not 100% perfect but should catch common - # installation issues like when the compiler was installed - # and then the install directory deleted or moved (rather - # than uninstalling properly), so the registry values - # are still there. - ok = False - for try_abi in ('IA32', 'IA32e', 'IA64', 'EM64T'): - try: - d = get_intel_registry_value('ProductDir', subkey, try_abi) - except MissingRegistryError: - continue # not found in reg, keep going - if os.path.exists(d): ok = True - if ok: - versions.append(subkey) - else: - try: - # Registry points to nonexistent dir. Ignore this - # version. - value = get_intel_registry_value('ProductDir', subkey, 'IA32') - except MissingRegistryError, e: - - # Registry key is left dangling (potentially - # after uninstalling). - - print \ - "scons: *** Ignoring the registry key for the Intel compiler version %s.\n" \ - "scons: *** It seems that the compiler was uninstalled and that the registry\n" \ - "scons: *** was not cleaned up properly.\n" % subkey - else: - print "scons: *** Ignoring "+str(value) - - i = i + 1 - except EnvironmentError: - # no more subkeys - pass - elif is_linux: - for d in glob.glob('/opt/intel_cc_*'): - # Typical dir here is /opt/intel_cc_80. - m = re.search(r'cc_(.*)$', d) - if m: - versions.append(m.group(1)) - for d in glob.glob('/opt/intel/cc*/*'): - # Typical dir here is /opt/intel/cc/9.0 for IA32, - # /opt/intel/cce/9.0 for EMT64 (AMD64) - m = re.search(r'([0-9][0-9.]*)$', d) - if m: - versions.append(m.group(1)) - for d in glob.glob('/opt/intel/Compiler/*'): - # Typical dir here is /opt/intel/Compiler/11.1 - m = re.search(r'([0-9][0-9.]*)$', d) - if m: - versions.append(m.group(1)) - elif is_mac: - for d in glob.glob('/opt/intel/cc*/*'): - # Typical dir here is /opt/intel/cc/9.0 for IA32, - # /opt/intel/cce/9.0 for EMT64 (AMD64) - m = re.search(r'([0-9][0-9.]*)$', d) - if m: - versions.append(m.group(1)) - def keyfunc(str): - """Given a dot-separated version string, return a tuple of ints representing it.""" - return [int(x) for x in str.split('.')] - # split into ints, sort, then remove dups - return sorted(uniquify(versions), key=keyfunc, reverse=True) - -def get_intel_compiler_top(version, abi): - """ - Return the main path to the top-level dir of the Intel compiler, - using the given version. - The compiler will be in /bin/icl.exe (icc on linux), - the include dir is /include, etc. - """ - - if is_windows: - if not SCons.Util.can_read_reg: - raise NoRegistryModuleError("No Windows registry module was found") - top = get_intel_registry_value('ProductDir', version, abi) - # pre-11, icl was in Bin. 11 and later, it's in Bin/ apparently. - if not os.path.exists(os.path.join(top, "Bin", "icl.exe")) \ - and not os.path.exists(os.path.join(top, "Bin", abi, "icl.exe")): - raise MissingDirError("Can't find Intel compiler in %s"%(top)) - elif is_mac or is_linux: - def find_in_2008style_dir(version): - # first dir is new (>=9.0) style, second is old (8.0) style. - dirs=('/opt/intel/cc/%s', '/opt/intel_cc_%s') - if abi == 'x86_64': - dirs=('/opt/intel/cce/%s',) # 'e' stands for 'em64t', aka x86_64 aka amd64 - top=None - for d in dirs: - if os.path.exists(os.path.join(d%version, "bin", "icc")): - top = d%version - break - return top - def find_in_2010style_dir(version): - dirs=('/opt/intel/Compiler/%s/*'%version) - # typically /opt/intel/Compiler/11.1/064 (then bin/intel64/icc) - dirs=glob.glob(dirs) - # find highest sub-version number by reverse sorting and picking first existing one. - dirs.sort() - dirs.reverse() - top=None - for d in dirs: - if (os.path.exists(os.path.join(d, "bin", "ia32", "icc")) or - os.path.exists(os.path.join(d, "bin", "intel64", "icc"))): - top = d - break - return top - top = find_in_2010style_dir(version) or find_in_2008style_dir(version) - print "INTELC: top=",top - if not top: - raise MissingDirError("Can't find version %s Intel compiler in %s (abi='%s')"%(version,top, abi)) - return top - - -def generate(env, version=None, abi=None, topdir=None, verbose=0): - """Add Builders and construction variables for Intel C/C++ compiler - to an Environment. - args: - version: (string) compiler version to use, like "80" - abi: (string) 'win32' or whatever Itanium version wants - topdir: (string) compiler top dir, like - "c:\Program Files\Intel\Compiler70" - If topdir is used, version and abi are ignored. - verbose: (int) if >0, prints compiler version used. - """ - if not (is_mac or is_linux or is_windows): - # can't handle this platform - return - - if is_windows: - SCons.Tool.msvc.generate(env) - elif is_linux: - SCons.Tool.gcc.generate(env) - elif is_mac: - SCons.Tool.gcc.generate(env) - - # if version is unspecified, use latest - vlist = get_all_compiler_versions() - if not version: - if vlist: - version = vlist[0] - else: - # User may have specified '90' but we need to get actual dirname '9.0'. - # get_version_from_list does that mapping. - v = get_version_from_list(version, vlist) - if not v: - raise SCons.Errors.UserError("Invalid Intel compiler version %s: "%version + \ - "installed versions are %s"%(', '.join(vlist))) - version = v - - # if abi is unspecified, use ia32 - # alternatives are ia64 for Itanium, or amd64 or em64t or x86_64 (all synonyms here) - abi = check_abi(abi) - if abi is None: - if is_mac or is_linux: - # Check if we are on 64-bit linux, default to 64 then. - uname_m = os.uname()[4] - if uname_m == 'x86_64': - abi = 'x86_64' - else: - abi = 'ia32' - else: - if is_win64: - abi = 'em64t' - else: - abi = 'ia32' - - if version and not topdir: - try: - topdir = get_intel_compiler_top(version, abi) - except (SCons.Util.RegError, IntelCError): - topdir = None - - if not topdir: - # Normally this is an error, but it might not be if the compiler is - # on $PATH and the user is importing their env. - class ICLTopDirWarning(SCons.Warnings.Warning): - pass - if (is_mac or is_linux) and not env.Detect('icc') or \ - is_windows and not env.Detect('icl'): - - SCons.Warnings.enableWarningClass(ICLTopDirWarning) - SCons.Warnings.warn(ICLTopDirWarning, - "Failed to find Intel compiler for version='%s', abi='%s'"% - (str(version), str(abi))) - else: - # should be cleaned up to say what this other version is - # since in this case we have some other Intel compiler installed - SCons.Warnings.enableWarningClass(ICLTopDirWarning) - SCons.Warnings.warn(ICLTopDirWarning, - "Can't find Intel compiler top dir for version='%s', abi='%s'"% - (str(version), str(abi))) - - if topdir: - archdir={'x86_64': 'intel64', - 'amd64' : 'intel64', - 'em64t' : 'intel64', - 'x86' : 'ia32', - 'i386' : 'ia32', - 'ia32' : 'ia32' - }[abi] # for v11 and greater - if os.path.exists(os.path.join(topdir, 'bin', archdir)): - bindir="bin/%s"%archdir - libdir="lib/%s"%archdir - else: - bindir="bin" - libdir="lib" - if verbose: - print "Intel C compiler: using version %s (%g), abi %s, in '%s/%s'"%\ - (repr(version), linux_ver_normalize(version),abi,topdir,bindir) - if is_linux: - # Show the actual compiler version by running the compiler. - os.system('%s/%s/icc --version'%(topdir,bindir)) - if is_mac: - # Show the actual compiler version by running the compiler. - os.system('%s/%s/icc --version'%(topdir,bindir)) - - env['INTEL_C_COMPILER_TOP'] = topdir - if is_linux: - paths={'INCLUDE' : 'include', - 'LIB' : libdir, - 'PATH' : bindir, - 'LD_LIBRARY_PATH' : libdir} - for p in paths.keys(): - env.PrependENVPath(p, os.path.join(topdir, paths[p])) - if is_mac: - paths={'INCLUDE' : 'include', - 'LIB' : libdir, - 'PATH' : bindir, - 'LD_LIBRARY_PATH' : libdir} - for p in paths.keys(): - env.PrependENVPath(p, os.path.join(topdir, paths[p])) - if is_windows: - # env key reg valname default subdir of top - paths=(('INCLUDE', 'IncludeDir', 'Include'), - ('LIB' , 'LibDir', 'Lib'), - ('PATH' , 'BinDir', 'Bin')) - # We are supposed to ignore version if topdir is set, so set - # it to the emptry string if it's not already set. - if version is None: - version = '' - # Each path has a registry entry, use that or default to subdir - for p in paths: - try: - path=get_intel_registry_value(p[1], version, abi) - # These paths may have $(ICInstallDir) - # which needs to be substituted with the topdir. - path=path.replace('$(ICInstallDir)', topdir + os.sep) - except IntelCError: - # Couldn't get it from registry: use default subdir of topdir - env.PrependENVPath(p[0], os.path.join(topdir, p[2])) - else: - env.PrependENVPath(p[0], path.split(os.pathsep)) - # print "ICL %s: %s, final=%s"%(p[0], path, str(env['ENV'][p[0]])) - - if is_windows: - env['CC'] = 'icl' - env['CXX'] = 'icl' - env['LINK'] = 'xilink' - else: - env['CC'] = 'icc' - env['CXX'] = 'icpc' - # Don't reset LINK here; - # use smart_link which should already be here from link.py. - #env['LINK'] = '$CC' - env['AR'] = 'xiar' - env['LD'] = 'xild' # not used by default - - # This is not the exact (detailed) compiler version, - # just the major version as determined above or specified - # by the user. It is a float like 80 or 90, in normalized form for Linux - # (i.e. even for Linux 9.0 compiler, still returns 90 rather than 9.0) - if version: - env['INTEL_C_COMPILER_VERSION']=linux_ver_normalize(version) - - if is_windows: - # Look for license file dir - # in system environment, registry, and default location. - envlicdir = os.environ.get("INTEL_LICENSE_FILE", '') - K = ('SOFTWARE\Intel\Licenses') - try: - k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, K) - reglicdir = SCons.Util.RegQueryValueEx(k, "w_cpp")[0] - except (AttributeError, SCons.Util.RegError): - reglicdir = "" - defaultlicdir = r'C:\Program Files\Common Files\Intel\Licenses' - - licdir = None - for ld in [envlicdir, reglicdir]: - # If the string contains an '@', then assume it's a network - # license (port@system) and good by definition. - if ld and (ld.find('@') != -1 or os.path.exists(ld)): - licdir = ld - break - if not licdir: - licdir = defaultlicdir - if not os.path.exists(licdir): - class ICLLicenseDirWarning(SCons.Warnings.Warning): - pass - SCons.Warnings.enableWarningClass(ICLLicenseDirWarning) - SCons.Warnings.warn(ICLLicenseDirWarning, - "Intel license dir was not found." - " Tried using the INTEL_LICENSE_FILE environment variable (%s), the registry (%s) and the default path (%s)." - " Using the default path as a last resort." - % (envlicdir, reglicdir, defaultlicdir)) - env['ENV']['INTEL_LICENSE_FILE'] = licdir - -def exists(env): - if not (is_mac or is_linux or is_windows): - # can't handle this platform - return 0 - - try: - versions = get_all_compiler_versions() - except (SCons.Util.RegError, IntelCError): - versions = None - detected = versions is not None and len(versions) > 0 - if not detected: - # try env.Detect, maybe that will work - if is_windows: - return env.Detect('icl') - elif is_linux: - return env.Detect('icc') - elif is_mac: - return env.Detect('icc') - return detected - -# end of file - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/ipkg.py b/scons/scons-local-2.2.0/SCons/Tool/ipkg.py deleted file mode 100644 index bc56dcd38..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/ipkg.py +++ /dev/null @@ -1,67 +0,0 @@ -"""SCons.Tool.ipkg - -Tool-specific initialization for ipkg. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -The ipkg tool calls the ipkg-build. Its only argument should be the -packages fake_root. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/ipkg.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os - -import SCons.Builder - -def generate(env): - """Add Builders and construction variables for ipkg to an Environment.""" - try: - bld = env['BUILDERS']['Ipkg'] - except KeyError: - bld = SCons.Builder.Builder( action = '$IPKGCOM', - suffix = '$IPKGSUFFIX', - source_scanner = None, - target_scanner = None) - env['BUILDERS']['Ipkg'] = bld - - env['IPKG'] = 'ipkg-build' - env['IPKGCOM'] = '$IPKG $IPKGFLAGS ${SOURCE}' - env['IPKGUSER'] = os.popen('id -un').read().strip() - env['IPKGGROUP'] = os.popen('id -gn').read().strip() - env['IPKGFLAGS'] = SCons.Util.CLVar('-o $IPKGUSER -g $IPKGGROUP') - env['IPKGSUFFIX'] = '.ipk' - -def exists(env): - return env.Detect('ipkg-build') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/jar.py b/scons/scons-local-2.2.0/SCons/Tool/jar.py deleted file mode 100644 index 321006c8f..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/jar.py +++ /dev/null @@ -1,116 +0,0 @@ -"""SCons.Tool.jar - -Tool-specific initialization for jar. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/jar.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Subst -import SCons.Util - -def jarSources(target, source, env, for_signature): - """Only include sources that are not a manifest file.""" - try: - env['JARCHDIR'] - except KeyError: - jarchdir_set = False - else: - jarchdir_set = True - jarchdir = env.subst('$JARCHDIR', target=target, source=source) - if jarchdir: - jarchdir = env.fs.Dir(jarchdir) - result = [] - for src in source: - contents = src.get_text_contents() - if contents[:16] != "Manifest-Version": - if jarchdir_set: - _chdir = jarchdir - else: - try: - _chdir = src.attributes.java_classdir - except AttributeError: - _chdir = None - if _chdir: - # If we are changing the dir with -C, then sources should - # be relative to that directory. - src = SCons.Subst.Literal(src.get_path(_chdir)) - result.append('-C') - result.append(_chdir) - result.append(src) - return result - -def jarManifest(target, source, env, for_signature): - """Look in sources for a manifest file, if any.""" - for src in source: - contents = src.get_text_contents() - if contents[:16] == "Manifest-Version": - return src - return '' - -def jarFlags(target, source, env, for_signature): - """If we have a manifest, make sure that the 'm' - flag is specified.""" - jarflags = env.subst('$JARFLAGS', target=target, source=source) - for src in source: - contents = src.get_text_contents() - if contents[:16] == "Manifest-Version": - if not 'm' in jarflags: - return jarflags + 'm' - break - return jarflags - -def generate(env): - """Add Builders and construction variables for jar to an Environment.""" - SCons.Tool.CreateJarBuilder(env) - - env['JAR'] = 'jar' - env['JARFLAGS'] = SCons.Util.CLVar('cf') - env['_JARFLAGS'] = jarFlags - env['_JARMANIFEST'] = jarManifest - env['_JARSOURCES'] = jarSources - env['_JARCOM'] = '$JAR $_JARFLAGS $TARGET $_JARMANIFEST $_JARSOURCES' - env['JARCOM'] = "${TEMPFILE('$_JARCOM')}" - env['JARSUFFIX'] = '.jar' - -def exists(env): - # As reported by Jan Nijtmans in issue #2730, the simple - # return env.Detect('jar') - # doesn't always work during initialization. For now, we - # stop trying to detect an executable (analogous to the - # javac Builder). - # TODO: Come up with a proper detect() routine...and enable it. - return 1 - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/javac.py b/scons/scons-local-2.2.0/SCons/Tool/javac.py deleted file mode 100644 index b682cbf0f..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/javac.py +++ /dev/null @@ -1,232 +0,0 @@ -"""SCons.Tool.javac - -Tool-specific initialization for javac. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Tool/javac.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os -import os.path - -import SCons.Action -import SCons.Builder -from SCons.Node.FS import _my_normcase -from SCons.Tool.JavaCommon import parse_java_file -import SCons.Util - -def classname(path): - """Turn a string (path name) into a Java class name.""" - return os.path.normpath(path).replace(os.sep, '.') - -def emit_java_classes(target, source, env): - """Create and return lists of source java files - and their corresponding target class files. - """ - java_suffix = env.get('JAVASUFFIX', '.java') - class_suffix = env.get('JAVACLASSSUFFIX', '.class') - - target[0].must_be_same(SCons.Node.FS.Dir) - classdir = target[0] - - s = source[0].rentry().disambiguate() - if isinstance(s, SCons.Node.FS.File): - sourcedir = s.dir.rdir() - elif isinstance(s, SCons.Node.FS.Dir): - sourcedir = s.rdir() - else: - raise SCons.Errors.UserError("Java source must be File or Dir, not '%s'" % s.__class__) - - slist = [] - js = _my_normcase(java_suffix) - for entry in source: - entry = entry.rentry().disambiguate() - if isinstance(entry, SCons.Node.FS.File): - slist.append(entry) - elif isinstance(entry, SCons.Node.FS.Dir): - result = SCons.Util.OrderedDict() - dirnode = entry.rdir() - def find_java_files(arg, dirpath, filenames): - java_files = sorted([n for n in filenames - if _my_normcase(n).endswith(js)]) - mydir = dirnode.Dir(dirpath) - java_paths = [mydir.File(f) for f in java_files] - for jp in java_paths: - arg[jp] = True - for dirpath, dirnames, filenames in os.walk(dirnode.get_abspath()): - find_java_files(result, dirpath, filenames) - entry.walk(find_java_files, result) - - slist.extend(list(result.keys())) - else: - raise SCons.Errors.UserError("Java source must be File or Dir, not '%s'" % entry.__class__) - - version = env.get('JAVAVERSION', '1.4') - full_tlist = [] - for f in slist: - tlist = [] - source_file_based = True - pkg_dir = None - if not f.is_derived(): - pkg_dir, classes = parse_java_file(f.rfile().get_abspath(), version) - if classes: - source_file_based = False - if pkg_dir: - d = target[0].Dir(pkg_dir) - p = pkg_dir + os.sep - else: - d = target[0] - p = '' - for c in classes: - t = d.File(c + class_suffix) - t.attributes.java_classdir = classdir - t.attributes.java_sourcedir = sourcedir - t.attributes.java_classname = classname(p + c) - tlist.append(t) - - if source_file_based: - base = f.name[:-len(java_suffix)] - if pkg_dir: - t = target[0].Dir(pkg_dir).File(base + class_suffix) - else: - t = target[0].File(base + class_suffix) - t.attributes.java_classdir = classdir - t.attributes.java_sourcedir = f.dir - t.attributes.java_classname = classname(base) - tlist.append(t) - - for t in tlist: - t.set_specific_source([f]) - - full_tlist.extend(tlist) - - return full_tlist, slist - -JavaAction = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR') - -JavaBuilder = SCons.Builder.Builder(action = JavaAction, - emitter = emit_java_classes, - target_factory = SCons.Node.FS.Entry, - source_factory = SCons.Node.FS.Entry) - -class pathopt(object): - """ - Callable object for generating javac-style path options from - a construction variable (e.g. -classpath, -sourcepath). - """ - def __init__(self, opt, var, default=None): - self.opt = opt - self.var = var - self.default = default - - def __call__(self, target, source, env, for_signature): - path = env[self.var] - if path and not SCons.Util.is_List(path): - path = [path] - if self.default: - default = env[self.default] - if default: - if not SCons.Util.is_List(default): - default = [default] - path = path + default - if path: - return [self.opt, os.pathsep.join(map(str, path))] - else: - return [] - -def Java(env, target, source, *args, **kw): - """ - A pseudo-Builder wrapper around the separate JavaClass{File,Dir} - Builders. - """ - if not SCons.Util.is_List(target): - target = [target] - if not SCons.Util.is_List(source): - source = [source] - - # Pad the target list with repetitions of the last element in the - # list so we have a target for every source element. - target = target + ([target[-1]] * (len(source) - len(target))) - - java_suffix = env.subst('$JAVASUFFIX') - result = [] - - for t, s in zip(target, source): - if isinstance(s, SCons.Node.FS.Base): - if isinstance(s, SCons.Node.FS.File): - b = env.JavaClassFile - else: - b = env.JavaClassDir - else: - if os.path.isfile(s): - b = env.JavaClassFile - elif os.path.isdir(s): - b = env.JavaClassDir - elif s[-len(java_suffix):] == java_suffix: - b = env.JavaClassFile - else: - b = env.JavaClassDir - result.extend(b(t, s, *args, **kw)) - - return result - -def generate(env): - """Add Builders and construction variables for javac to an Environment.""" - java_file = SCons.Tool.CreateJavaFileBuilder(env) - java_class = SCons.Tool.CreateJavaClassFileBuilder(env) - java_class_dir = SCons.Tool.CreateJavaClassDirBuilder(env) - java_class.add_emitter(None, emit_java_classes) - java_class.add_emitter(env.subst('$JAVASUFFIX'), emit_java_classes) - java_class_dir.emitter = emit_java_classes - - env.AddMethod(Java) - - env['JAVAC'] = 'javac' - env['JAVACFLAGS'] = SCons.Util.CLVar('') - env['JAVABOOTCLASSPATH'] = [] - env['JAVACLASSPATH'] = [] - env['JAVASOURCEPATH'] = [] - env['_javapathopt'] = pathopt - env['_JAVABOOTCLASSPATH'] = '${_javapathopt("-bootclasspath", "JAVABOOTCLASSPATH")} ' - env['_JAVACLASSPATH'] = '${_javapathopt("-classpath", "JAVACLASSPATH")} ' - env['_JAVASOURCEPATH'] = '${_javapathopt("-sourcepath", "JAVASOURCEPATH", "_JAVASOURCEPATHDEFAULT")} ' - env['_JAVASOURCEPATHDEFAULT'] = '${TARGET.attributes.java_sourcedir}' - env['_JAVACCOM'] = '$JAVAC $JAVACFLAGS $_JAVABOOTCLASSPATH $_JAVACLASSPATH -d ${TARGET.attributes.java_classdir} $_JAVASOURCEPATH $SOURCES' - env['JAVACCOM'] = "${TEMPFILE('$_JAVACCOM')}" - env['JAVACLASSSUFFIX'] = '.class' - env['JAVASUFFIX'] = '.java' - -def exists(env): - return 1 - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/javah.py b/scons/scons-local-2.2.0/SCons/Tool/javah.py deleted file mode 100644 index be5145d48..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/javah.py +++ /dev/null @@ -1,137 +0,0 @@ -"""SCons.Tool.javah - -Tool-specific initialization for javah. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/javah.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os.path - -import SCons.Action -import SCons.Builder -import SCons.Node.FS -import SCons.Tool.javac -import SCons.Util - -def emit_java_headers(target, source, env): - """Create and return lists of Java stub header files that will - be created from a set of class files. - """ - class_suffix = env.get('JAVACLASSSUFFIX', '.class') - classdir = env.get('JAVACLASSDIR') - - if not classdir: - try: - s = source[0] - except IndexError: - classdir = '.' - else: - try: - classdir = s.attributes.java_classdir - except AttributeError: - classdir = '.' - classdir = env.Dir(classdir).rdir() - - if str(classdir) == '.': - c_ = None - else: - c_ = str(classdir) + os.sep - - slist = [] - for src in source: - try: - classname = src.attributes.java_classname - except AttributeError: - classname = str(src) - if c_ and classname[:len(c_)] == c_: - classname = classname[len(c_):] - if class_suffix and classname[-len(class_suffix):] == class_suffix: - classname = classname[:-len(class_suffix)] - classname = SCons.Tool.javac.classname(classname) - s = src.rfile() - s.attributes.java_classname = classname - slist.append(s) - - s = source[0].rfile() - if not hasattr(s.attributes, 'java_classdir'): - s.attributes.java_classdir = classdir - - if target[0].__class__ is SCons.Node.FS.File: - tlist = target - else: - if not isinstance(target[0], SCons.Node.FS.Dir): - target[0].__class__ = SCons.Node.FS.Dir - target[0]._morph() - tlist = [] - for s in source: - fname = s.attributes.java_classname.replace('.', '_') + '.h' - t = target[0].File(fname) - t.attributes.java_lookupdir = target[0] - tlist.append(t) - - return tlist, source - -def JavaHOutFlagGenerator(target, source, env, for_signature): - try: - t = target[0] - except (AttributeError, IndexError, TypeError): - t = target - try: - return '-d ' + str(t.attributes.java_lookupdir) - except AttributeError: - return '-o ' + str(t) - -def getJavaHClassPath(env,target, source, for_signature): - path = "${SOURCE.attributes.java_classdir}" - if 'JAVACLASSPATH' in env and env['JAVACLASSPATH']: - path = SCons.Util.AppendPath(path, env['JAVACLASSPATH']) - return "-classpath %s" % (path) - -def generate(env): - """Add Builders and construction variables for javah to an Environment.""" - java_javah = SCons.Tool.CreateJavaHBuilder(env) - java_javah.emitter = emit_java_headers - - env['_JAVAHOUTFLAG'] = JavaHOutFlagGenerator - env['JAVAH'] = 'javah' - env['JAVAHFLAGS'] = SCons.Util.CLVar('') - env['_JAVAHCLASSPATH'] = getJavaHClassPath - env['JAVAHCOM'] = '$JAVAH $JAVAHFLAGS $_JAVAHOUTFLAG $_JAVAHCLASSPATH ${SOURCES.attributes.java_classname}' - env['JAVACLASSSUFFIX'] = '.class' - -def exists(env): - return env.Detect('javah') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/latex.py b/scons/scons-local-2.2.0/SCons/Tool/latex.py deleted file mode 100644 index 427c37324..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/latex.py +++ /dev/null @@ -1,80 +0,0 @@ -"""SCons.Tool.latex - -Tool-specific initialization for LaTeX. -Generates .dvi files from .latex or .ltx files - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/latex.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Action -import SCons.Defaults -import SCons.Scanner.LaTeX -import SCons.Util -import SCons.Tool -import SCons.Tool.tex - -def LaTeXAuxFunction(target = None, source= None, env=None): - result = SCons.Tool.tex.InternalLaTeXAuxAction( SCons.Tool.tex.LaTeXAction, target, source, env ) - if result != 0: - SCons.Tool.tex.check_file_error_message(env['LATEX']) - return result - -LaTeXAuxAction = SCons.Action.Action(LaTeXAuxFunction, - strfunction=SCons.Tool.tex.TeXLaTeXStrFunction) - -def generate(env): - """Add Builders and construction variables for LaTeX to an Environment.""" - - env.AppendUnique(LATEXSUFFIXES=SCons.Tool.LaTeXSuffixes) - - import dvi - dvi.generate(env) - - import pdf - pdf.generate(env) - - bld = env['BUILDERS']['DVI'] - bld.add_action('.ltx', LaTeXAuxAction) - bld.add_action('.latex', LaTeXAuxAction) - bld.add_emitter('.ltx', SCons.Tool.tex.tex_eps_emitter) - bld.add_emitter('.latex', SCons.Tool.tex.tex_eps_emitter) - - SCons.Tool.tex.generate_common(env) - -def exists(env): - SCons.Tool.tex.generate_darwin(env) - return env.Detect('latex') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/lex.py b/scons/scons-local-2.2.0/SCons/Tool/lex.py deleted file mode 100644 index 76e899248..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/lex.py +++ /dev/null @@ -1,97 +0,0 @@ -"""SCons.Tool.lex - -Tool-specific initialization for lex. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/lex.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os.path - -import SCons.Action -import SCons.Tool -import SCons.Util - -LexAction = SCons.Action.Action("$LEXCOM", "$LEXCOMSTR") - -def lexEmitter(target, source, env): - sourceBase, sourceExt = os.path.splitext(SCons.Util.to_String(source[0])) - - if sourceExt == ".lm": # If using Objective-C - target = [sourceBase + ".m"] # the extension is ".m". - - # This emitter essentially tries to add to the target all extra - # files generated by flex. - - # Different options that are used to trigger the creation of extra files. - fileGenOptions = ["--header-file=", "--tables-file="] - - lexflags = env.subst("$LEXFLAGS", target=target, source=source) - for option in SCons.Util.CLVar(lexflags): - for fileGenOption in fileGenOptions: - l = len(fileGenOption) - if option[:l] == fileGenOption: - # A file generating option is present, so add the - # file name to the target list. - fileName = option[l:].strip() - target.append(fileName) - return (target, source) - -def generate(env): - """Add Builders and construction variables for lex to an Environment.""" - c_file, cxx_file = SCons.Tool.createCFileBuilders(env) - - # C - c_file.add_action(".l", LexAction) - c_file.add_emitter(".l", lexEmitter) - - c_file.add_action(".lex", LexAction) - c_file.add_emitter(".lex", lexEmitter) - - # Objective-C - cxx_file.add_action(".lm", LexAction) - cxx_file.add_emitter(".lm", lexEmitter) - - # C++ - cxx_file.add_action(".ll", LexAction) - cxx_file.add_emitter(".ll", lexEmitter) - - env["LEX"] = env.Detect("flex") or "lex" - env["LEXFLAGS"] = SCons.Util.CLVar("") - env["LEXCOM"] = "$LEX $LEXFLAGS -t $SOURCES > $TARGET" - -def exists(env): - return env.Detect(["flex", "lex"]) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/link.py b/scons/scons-local-2.2.0/SCons/Tool/link.py deleted file mode 100644 index 13a671d76..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/link.py +++ /dev/null @@ -1,122 +0,0 @@ -"""SCons.Tool.link - -Tool-specific initialization for the generic Posix linker. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/link.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Defaults -import SCons.Tool -import SCons.Util -import SCons.Warnings - -from SCons.Tool.FortranCommon import isfortran - -cplusplus = __import__('c++', globals(), locals(), []) - -issued_mixed_link_warning = False - -def smart_link(source, target, env, for_signature): - has_cplusplus = cplusplus.iscplusplus(source) - has_fortran = isfortran(env, source) - if has_cplusplus and has_fortran: - global issued_mixed_link_warning - if not issued_mixed_link_warning: - msg = "Using $CXX to link Fortran and C++ code together.\n\t" + \ - "This may generate a buggy executable if the '%s'\n\t" + \ - "compiler does not know how to deal with Fortran runtimes." - SCons.Warnings.warn(SCons.Warnings.FortranCxxMixWarning, - msg % env.subst('$CXX')) - issued_mixed_link_warning = True - return '$CXX' - elif has_fortran: - return '$FORTRAN' - elif has_cplusplus: - return '$CXX' - return '$CC' - -def shlib_emitter(target, source, env): - for tgt in target: - tgt.attributes.shared = 1 - return (target, source) - -def generate(env): - """Add Builders and construction variables for gnulink to an Environment.""" - SCons.Tool.createSharedLibBuilder(env) - SCons.Tool.createProgBuilder(env) - - env['SHLINK'] = '$LINK' - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') - env['SHLINKCOM'] = '$SHLINK -o $TARGET $SHLINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' - # don't set up the emitter, cause AppendUnique will generate a list - # starting with None :-( - env.Append(SHLIBEMITTER = [shlib_emitter]) - env['SMARTLINK'] = smart_link - env['LINK'] = "$SMARTLINK" - env['LINKFLAGS'] = SCons.Util.CLVar('') - # __RPATH is only set to something ($_RPATH typically) on platforms that support it. - env['LINKCOM'] = '$LINK -o $TARGET $LINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' - env['LIBDIRPREFIX']='-L' - env['LIBDIRSUFFIX']='' - env['_LIBFLAGS']='${_stripixes(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, LIBPREFIXES, LIBSUFFIXES, __env__)}' - env['LIBLINKPREFIX']='-l' - env['LIBLINKSUFFIX']='' - - if env['PLATFORM'] == 'hpux': - env['SHLIBSUFFIX'] = '.sl' - elif env['PLATFORM'] == 'aix': - env['SHLIBSUFFIX'] = '.a' - - # For most platforms, a loadable module is the same as a shared - # library. Platforms which are different can override these, but - # setting them the same means that LoadableModule works everywhere. - SCons.Tool.createLoadableModuleBuilder(env) - env['LDMODULE'] = '$SHLINK' - # don't set up the emitter, cause AppendUnique will generate a list - # starting with None :-( - env.Append(LDMODULEEMITTER='$SHLIBEMITTER') - env['LDMODULEPREFIX'] = '$SHLIBPREFIX' - env['LDMODULESUFFIX'] = '$SHLIBSUFFIX' - env['LDMODULEFLAGS'] = '$SHLINKFLAGS' - env['LDMODULECOM'] = '$LDMODULE -o $TARGET $LDMODULEFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' - - - -def exists(env): - # This module isn't really a Tool on its own, it's common logic for - # other linkers. - return None - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/linkloc.py b/scons/scons-local-2.2.0/SCons/Tool/linkloc.py deleted file mode 100644 index 50a1a5159..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/linkloc.py +++ /dev/null @@ -1,112 +0,0 @@ -"""SCons.Tool.linkloc - -Tool specification for the LinkLoc linker for the Phar Lap ETS embedded -operating system. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/linkloc.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os.path -import re - -import SCons.Action -import SCons.Defaults -import SCons.Errors -import SCons.Tool -import SCons.Util - -from SCons.Tool.MSCommon import msvs_exists, merge_default_version -from SCons.Tool.PharLapCommon import addPharLapPaths - -_re_linker_command = re.compile(r'(\s)@\s*([^\s]+)') - -def repl_linker_command(m): - # Replaces any linker command file directives (e.g. "@foo.lnk") with - # the actual contents of the file. - try: - f=open(m.group(2), "r") - return m.group(1) + f.read() - except IOError: - # the linker should return an error if it can't - # find the linker command file so we will remain quiet. - # However, we will replace the @ with a # so we will not continue - # to find it with recursive substitution - return m.group(1) + '#' + m.group(2) - -class LinklocGenerator(object): - def __init__(self, cmdline): - self.cmdline = cmdline - - def __call__(self, env, target, source, for_signature): - if for_signature: - # Expand the contents of any linker command files recursively - subs = 1 - strsub = env.subst(self.cmdline, target=target, source=source) - while subs: - strsub, subs = _re_linker_command.subn(repl_linker_command, strsub) - return strsub - else: - return "${TEMPFILE('" + self.cmdline + "')}" - -def generate(env): - """Add Builders and construction variables for ar to an Environment.""" - SCons.Tool.createSharedLibBuilder(env) - SCons.Tool.createProgBuilder(env) - - env['SUBST_CMD_FILE'] = LinklocGenerator - env['SHLINK'] = '$LINK' - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS') - env['SHLINKCOM'] = '${SUBST_CMD_FILE("$SHLINK $SHLINKFLAGS $_LIBDIRFLAGS $_LIBFLAGS -dll $TARGET $SOURCES")}' - env['SHLIBEMITTER']= None - env['LINK'] = "linkloc" - env['LINKFLAGS'] = SCons.Util.CLVar('') - env['LINKCOM'] = '${SUBST_CMD_FILE("$LINK $LINKFLAGS $_LIBDIRFLAGS $_LIBFLAGS -exe $TARGET $SOURCES")}' - env['LIBDIRPREFIX']='-libpath ' - env['LIBDIRSUFFIX']='' - env['LIBLINKPREFIX']='-lib ' - env['LIBLINKSUFFIX']='$LIBSUFFIX' - - # Set-up ms tools paths for default version - merge_default_version(env) - - addPharLapPaths(env) - -def exists(env): - if msvs_exists(): - return env.Detect('linkloc') - else: - return 0 - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/m4.py b/scons/scons-local-2.2.0/SCons/Tool/m4.py deleted file mode 100644 index 9bd4ef7c8..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/m4.py +++ /dev/null @@ -1,63 +0,0 @@ -"""SCons.Tool.m4 - -Tool-specific initialization for m4. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/m4.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Action -import SCons.Builder -import SCons.Util - -def generate(env): - """Add Builders and construction variables for m4 to an Environment.""" - M4Action = SCons.Action.Action('$M4COM', '$M4COMSTR') - bld = SCons.Builder.Builder(action = M4Action, src_suffix = '.m4') - - env['BUILDERS']['M4'] = bld - - # .m4 files might include other files, and it would be pretty hard - # to write a scanner for it, so let's just cd to the dir of the m4 - # file and run from there. - # The src_suffix setup is like so: file.c.m4 -> file.c, - # file.cpp.m4 -> file.cpp etc. - env['M4'] = 'm4' - env['M4FLAGS'] = SCons.Util.CLVar('-E') - env['M4COM'] = 'cd ${SOURCE.rsrcdir} && $M4 $M4FLAGS < ${SOURCE.file} > ${TARGET.abspath}' - -def exists(env): - return env.Detect('m4') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/masm.py b/scons/scons-local-2.2.0/SCons/Tool/masm.py deleted file mode 100644 index f41f700a2..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/masm.py +++ /dev/null @@ -1,77 +0,0 @@ -"""SCons.Tool.masm - -Tool-specific initialization for the Microsoft Assembler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/masm.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Defaults -import SCons.Tool -import SCons.Util - -ASSuffixes = ['.s', '.asm', '.ASM'] -ASPPSuffixes = ['.spp', '.SPP', '.sx'] -if SCons.Util.case_sensitive_suffixes('.s', '.S'): - ASPPSuffixes.extend(['.S']) -else: - ASSuffixes.extend(['.S']) - -def generate(env): - """Add Builders and construction variables for masm to an Environment.""" - static_obj, shared_obj = SCons.Tool.createObjBuilders(env) - - for suffix in ASSuffixes: - static_obj.add_action(suffix, SCons.Defaults.ASAction) - shared_obj.add_action(suffix, SCons.Defaults.ASAction) - static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) - shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) - - for suffix in ASPPSuffixes: - static_obj.add_action(suffix, SCons.Defaults.ASPPAction) - shared_obj.add_action(suffix, SCons.Defaults.ASPPAction) - static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) - shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) - - env['AS'] = 'ml' - env['ASFLAGS'] = SCons.Util.CLVar('/nologo') - env['ASPPFLAGS'] = '$ASFLAGS' - env['ASCOM'] = '$AS $ASFLAGS /c /Fo$TARGET $SOURCES' - env['ASPPCOM'] = '$CC $ASPPFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c /Fo$TARGET $SOURCES' - env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 - -def exists(env): - return env.Detect('ml') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/midl.py b/scons/scons-local-2.2.0/SCons/Tool/midl.py deleted file mode 100644 index 2cdcd5ae1..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/midl.py +++ /dev/null @@ -1,88 +0,0 @@ -"""SCons.Tool.midl - -Tool-specific initialization for midl (Microsoft IDL compiler). - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/midl.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Action -import SCons.Builder -import SCons.Defaults -import SCons.Scanner.IDL -import SCons.Util - -from MSCommon import msvc_exists - -def midl_emitter(target, source, env): - """Produces a list of outputs from the MIDL compiler""" - base, ext = SCons.Util.splitext(str(target[0])) - tlb = target[0] - incl = base + '.h' - interface = base + '_i.c' - t = [tlb, incl, interface] - - midlcom = env['MIDLCOM'] - - if midlcom.find('/proxy') != -1: - proxy = base + '_p.c' - t.append(proxy) - if midlcom.find('/dlldata') != -1: - dlldata = base + '_data.c' - t.append(dlldata) - - return (t,source) - -idl_scanner = SCons.Scanner.IDL.IDLScan() - -midl_action = SCons.Action.Action('$MIDLCOM', '$MIDLCOMSTR') - -midl_builder = SCons.Builder.Builder(action = midl_action, - src_suffix = '.idl', - suffix='.tlb', - emitter = midl_emitter, - source_scanner = idl_scanner) - -def generate(env): - """Add Builders and construction variables for midl to an Environment.""" - - env['MIDL'] = 'MIDL.EXE' - env['MIDLFLAGS'] = SCons.Util.CLVar('/nologo') - env['MIDLCOM'] = '$MIDL $MIDLFLAGS /tlb ${TARGETS[0]} /h ${TARGETS[1]} /iid ${TARGETS[2]} /proxy ${TARGETS[3]} /dlldata ${TARGETS[4]} $SOURCE 2> NUL' - env['BUILDERS']['TypeLibrary'] = midl_builder - -def exists(env): - return msvc_exists() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/mingw.py b/scons/scons-local-2.2.0/SCons/Tool/mingw.py deleted file mode 100644 index 1c9c0829f..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/mingw.py +++ /dev/null @@ -1,179 +0,0 @@ -"""SCons.Tool.gcc - -Tool-specific initialization for MinGW (http://www.mingw.org/) - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/mingw.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os -import os.path - -import SCons.Action -import SCons.Builder -import SCons.Defaults -import SCons.Tool -import SCons.Util - -# This is what we search for to find mingw: -key_program = 'mingw32-gcc' - -def find(env): - # First search in the SCons path - path=env.WhereIs(key_program) - if (path): - return path - # then the OS path: - path=SCons.Util.WhereIs(key_program) - if (path): - return path - - # If that doesn't work try default location for mingw - save_path=env['ENV']['PATH'] - env.AppendENVPath('PATH',r'c:\MinGW\bin') - path =env.WhereIs(key_program) - if not path: - env['ENV']['PATH']=save_path - return path - -def shlib_generator(target, source, env, for_signature): - cmd = SCons.Util.CLVar(['$SHLINK', '$SHLINKFLAGS']) - - dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') - if dll: cmd.extend(['-o', dll]) - - cmd.extend(['$SOURCES', '$_LIBDIRFLAGS', '$_LIBFLAGS']) - - implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX') - if implib: cmd.append('-Wl,--out-implib,'+implib.get_string(for_signature)) - - def_target = env.FindIxes(target, 'WINDOWSDEFPREFIX', 'WINDOWSDEFSUFFIX') - insert_def = env.subst("$WINDOWS_INSERT_DEF") - if not insert_def in ['', '0', 0] and def_target: \ - cmd.append('-Wl,--output-def,'+def_target.get_string(for_signature)) - - return [cmd] - -def shlib_emitter(target, source, env): - dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') - no_import_lib = env.get('no_import_lib', 0) - - if not dll: - raise SCons.Errors.UserError("A shared library should have exactly one target with the suffix: %s" % env.subst("$SHLIBSUFFIX")) - - if not no_import_lib and \ - not env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX'): - - # Create list of target libraries as strings - targetStrings=env.ReplaceIxes(dll, - 'SHLIBPREFIX', 'SHLIBSUFFIX', - 'LIBPREFIX', 'LIBSUFFIX') - - # Now add file nodes to target list - target.append(env.fs.File(targetStrings)) - - # Append a def file target if there isn't already a def file target - # or a def file source or the user has explicitly asked for the target - # to be emitted. - def_source = env.FindIxes(source, 'WINDOWSDEFPREFIX', 'WINDOWSDEFSUFFIX') - def_target = env.FindIxes(target, 'WINDOWSDEFPREFIX', 'WINDOWSDEFSUFFIX') - skip_def_insert = env.subst("$WINDOWS_INSERT_DEF") in ['', '0', 0] - if not def_source and not def_target and not skip_def_insert: - # Create list of target libraries and def files as strings - targetStrings=env.ReplaceIxes(dll, - 'SHLIBPREFIX', 'SHLIBSUFFIX', - 'WINDOWSDEFPREFIX', 'WINDOWSDEFSUFFIX') - - # Now add file nodes to target list - target.append(env.fs.File(targetStrings)) - - return (target, source) - - -shlib_action = SCons.Action.Action(shlib_generator, generator=1) - -res_action = SCons.Action.Action('$RCCOM', '$RCCOMSTR') - -res_builder = SCons.Builder.Builder(action=res_action, suffix='.o', - source_scanner=SCons.Tool.SourceFileScanner) -SCons.Tool.SourceFileScanner.add_scanner('.rc', SCons.Defaults.CScan) - -def generate(env): - mingw = find(env) - if mingw: - dir = os.path.dirname(mingw) - env.PrependENVPath('PATH', dir ) - - - # Most of mingw is the same as gcc and friends... - gnu_tools = ['gcc', 'g++', 'gnulink', 'ar', 'gas', 'm4'] - for tool in gnu_tools: - SCons.Tool.Tool(tool)(env) - - #... but a few things differ: - env['CC'] = 'gcc' - env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') - env['CXX'] = 'g++' - env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') - env['SHLINKCOM'] = shlib_action - env['LDMODULECOM'] = shlib_action - env.Append(SHLIBEMITTER = [shlib_emitter]) - env['AS'] = 'as' - - env['WIN32DEFPREFIX'] = '' - env['WIN32DEFSUFFIX'] = '.def' - env['WINDOWSDEFPREFIX'] = '${WIN32DEFPREFIX}' - env['WINDOWSDEFSUFFIX'] = '${WIN32DEFSUFFIX}' - - env['SHOBJSUFFIX'] = '.o' - env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 - - env['RC'] = 'windres' - env['RCFLAGS'] = SCons.Util.CLVar('') - env['RCINCFLAGS'] = '$( ${_concat(RCINCPREFIX, CPPPATH, RCINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' - env['RCINCPREFIX'] = '--include-dir ' - env['RCINCSUFFIX'] = '' - env['RCCOM'] = '$RC $_CPPDEFFLAGS $RCINCFLAGS ${RCINCPREFIX} ${SOURCE.dir} $RCFLAGS -i $SOURCE -o $TARGET' - env['BUILDERS']['RES'] = res_builder - - # Some setting from the platform also have to be overridden: - env['OBJSUFFIX'] = '.o' - env['LIBPREFIX'] = 'lib' - env['LIBSUFFIX'] = '.a' - -def exists(env): - return find(env) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/msgfmt.py b/scons/scons-local-2.2.0/SCons/Tool/msgfmt.py deleted file mode 100644 index 4fcd8fcc4..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/msgfmt.py +++ /dev/null @@ -1,102 +0,0 @@ -""" msgfmt tool """ - -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Tool/msgfmt.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -from SCons.Builder import BuilderBase -############################################################################# -class _MOFileBuilder(BuilderBase): - """ The builder class for `MO` files. - - The reason for this builder to exists and its purpose is quite simillar - as for `_POFileBuilder`. This time, we extend list of sources, not targets, - and call `BuilderBase._execute()` only once (as we assume single-target - here). - """ - - def _execute(self, env, target, source, *args, **kw): - # Here we add support for 'LINGUAS_FILE' keyword. Emitter is not suitable - # in this case, as it is called too late (after multiple sources - # are handled single_source builder. - import SCons.Util - from SCons.Tool.GettextCommon import _read_linguas_from_files - linguas_files = None - if env.has_key('LINGUAS_FILE') and env['LINGUAS_FILE'] is not None: - linguas_files = env['LINGUAS_FILE'] - # This should prevent from endless recursion. - env['LINGUAS_FILE'] = None - # We read only languages. Suffixes shall be added automatically. - linguas = _read_linguas_from_files(env, linguas_files) - if SCons.Util.is_List(source): - source.extend(linguas) - elif source is not None: - source = [source] + linguas - else: - source = linguas - result = BuilderBase._execute(self,env,target,source,*args, **kw) - if linguas_files is not None: - env['LINGUAS_FILE'] = linguas_files - return result -############################################################################# - -############################################################################# -def _create_mo_file_builder(env, **kw): - """ Create builder object for `MOFiles` builder """ - import SCons.Action - # FIXME: What factory use for source? Ours or their? - kw['action'] = SCons.Action.Action('$MSGFMTCOM','$MSGFMTCOMSTR') - kw['suffix'] = '$MOSUFFIX' - kw['src_suffix'] = '$POSUFFIX' - kw['src_builder'] = '_POUpdateBuilder' - kw['single_source'] = True - return _MOFileBuilder(**kw) -############################################################################# - -############################################################################# -def generate(env,**kw): - """ Generate `msgfmt` tool """ - import SCons.Util - from SCons.Tool.GettextCommon import _detect_msgfmt - env['MSGFMT'] = _detect_msgfmt(env) - env.SetDefault( - MSGFMTFLAGS = [ SCons.Util.CLVar('-c') ], - MSGFMTCOM = '$MSGFMT $MSGFMTFLAGS -o $TARGET $SOURCE', - MSGFMTCOMSTR = '', - MOSUFFIX = ['.mo'], - POSUFFIX = ['.po'] - ) - env.Append( BUILDERS = { 'MOFiles' : _create_mo_file_builder(env) } ) -############################################################################# - -############################################################################# -def exists(env): - """ Check if the tool exists """ - from SCons.Tool.GettextCommon import _msgfmt_exists - return _msgfmt_exists(env) -############################################################################# - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/msginit.py b/scons/scons-local-2.2.0/SCons/Tool/msginit.py deleted file mode 100644 index 210fbca58..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/msginit.py +++ /dev/null @@ -1,114 +0,0 @@ -""" msginit tool - -Tool specific initialization of msginit tool. -""" - -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Tool/msginit.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Warnings -import SCons.Builder -import re - -############################################################################# -def _optional_no_translator_flag(env): - """ Return '--no-translator' flag if we run *msginit(1)* in non-interactive - mode.""" - import SCons.Util - if env.has_key('POAUTOINIT'): - autoinit = env['POAUTOINIT'] - else: - autoinit = False - if autoinit: - return [SCons.Util.CLVar('--no-translator')] - else: - return [SCons.Util.CLVar('')] -############################################################################# - -############################################################################# -def _POInitBuilder(env, **kw): - """ Create builder object for `POInit` builder. """ - import SCons.Action - from SCons.Tool.GettextCommon import _init_po_files, _POFileBuilder - action = SCons.Action.Action(_init_po_files, None) - return _POFileBuilder(env, action=action, target_alias='$POCREATE_ALIAS') -############################################################################# - -############################################################################# -from SCons.Environment import _null -############################################################################# -def _POInitBuilderWrapper(env, target=None, source=_null, **kw): - """ Wrapper for _POFileBuilder. We use it to make user's life easier. - - This wrapper checks for `$POTDOMAIN` construction variable (or override in - `**kw`) and treats it appropriatelly. - """ - if source is _null: - if 'POTDOMAIN' in kw: - domain = kw['POTDOMAIN'] - elif env.has_key('POTDOMAIN'): - domain = env['POTDOMAIN'] - else: - domain = 'messages' - source = [ domain ] # NOTE: Suffix shall be appended automatically - return env._POInitBuilder(target, source, **kw) -############################################################################# - -############################################################################# -def generate(env,**kw): - """ Generate the `msginit` tool """ - import SCons.Util - from SCons.Tool.GettextCommon import _detect_msginit - env['MSGINIT'] = _detect_msginit(env) - msginitcom = '$MSGINIT ${_MSGNoTranslator(__env__)} -l ${_MSGINITLOCALE}' \ - + ' $MSGINITFLAGS -i $SOURCE -o $TARGET' - # NOTE: We set POTSUFFIX here, in case the 'xgettext' is not loaded - # (sometimes we really don't need it) - env.SetDefault( - POSUFFIX = ['.po'], - POTSUFFIX = ['.pot'], - _MSGINITLOCALE = '${TARGET.filebase}', - _MSGNoTranslator = _optional_no_translator_flag, - MSGINITCOM = msginitcom, - MSGINITCOMSTR = '', - MSGINITFLAGS = [ ], - POAUTOINIT = False, - POCREATE_ALIAS = 'po-create' - ) - env.Append( BUILDERS = { '_POInitBuilder' : _POInitBuilder(env) } ) - env.AddMethod(_POInitBuilderWrapper, 'POInit') - env.AlwaysBuild(env.Alias('$POCREATE_ALIAS')) -############################################################################# - -############################################################################# -def exists(env): - """ Check if the tool exists """ - from SCons.Tool.GettextCommon import _msginit_exists - return _msginit_exists(env) -############################################################################# - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/msgmerge.py b/scons/scons-local-2.2.0/SCons/Tool/msgmerge.py deleted file mode 100644 index 2bc89f4d5..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/msgmerge.py +++ /dev/null @@ -1,98 +0,0 @@ -""" msgmerget tool - -Tool specific initialization for `msgmerge` tool. -""" - -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Tool/msgmerge.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -############################################################################# -def _update_or_init_po_files(target, source, env): - """ Action function for `POUpdate` builder """ - import SCons.Action - from SCons.Tool.GettextCommon import _init_po_files - for tgt in target: - if tgt.rexists(): - action = SCons.Action.Action('$MSGMERGECOM', '$MSGMERGECOMSTR') - else: - action = _init_po_files - status = action([tgt], source, env) - if status : return status - return 0 -############################################################################# - -############################################################################# -def _POUpdateBuilder(env, **kw): - """ Create an object of `POUpdate` builder """ - import SCons.Action - from SCons.Tool.GettextCommon import _POFileBuilder - action = SCons.Action.Action(_update_or_init_po_files, None) - return _POFileBuilder(env, action=action, target_alias='$POUPDATE_ALIAS') -############################################################################# - -############################################################################# -from SCons.Environment import _null -############################################################################# -def _POUpdateBuilderWrapper(env, target=None, source=_null, **kw): - """ Wrapper for `POUpdate` builder - make user's life easier """ - if source is _null: - if 'POTDOMAIN' in kw: - domain = kw['POTDOMAIN'] - elif env.has_key('POTDOMAIN') and env['POTDOMAIN']: - domain = env['POTDOMAIN'] - else: - domain = 'messages' - source = [ domain ] # NOTE: Suffix shall be appended automatically - return env._POUpdateBuilder(target, source, **kw) -############################################################################# - -############################################################################# -def generate(env,**kw): - """ Generate the `xgettext` tool """ - from SCons.Tool.GettextCommon import _detect_msgmerge - env['MSGMERGE'] = _detect_msgmerge(env) - env.SetDefault( - POTSUFFIX = ['.pot'], - POSUFFIX = ['.po'], - MSGMERGECOM = '$MSGMERGE $MSGMERGEFLAGS --update $TARGET $SOURCE', - MSGMERGECOMSTR = '', - MSGMERGEFLAGS = [ ], - POUPDATE_ALIAS = 'po-update' - ) - env.Append(BUILDERS = { '_POUpdateBuilder':_POUpdateBuilder(env) }) - env.AddMethod(_POUpdateBuilderWrapper, 'POUpdate') - env.AlwaysBuild(env.Alias('$POUPDATE_ALIAS')) -############################################################################# - -############################################################################# -def exists(env): - """ Check if the tool exists """ - from SCons.Tool.GettextCommon import _msgmerge_exists - return _msgmerge_exists(env) -############################################################################# - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/mslib.py b/scons/scons-local-2.2.0/SCons/Tool/mslib.py deleted file mode 100644 index 82ea50323..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/mslib.py +++ /dev/null @@ -1,64 +0,0 @@ -"""SCons.Tool.mslib - -Tool-specific initialization for lib (MicroSoft library archiver). - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/mslib.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Defaults -import SCons.Tool -import SCons.Tool.msvs -import SCons.Tool.msvc -import SCons.Util - -from MSCommon import msvc_exists, msvc_setup_env_once - -def generate(env): - """Add Builders and construction variables for lib to an Environment.""" - SCons.Tool.createStaticLibBuilder(env) - - # Set-up ms tools paths - msvc_setup_env_once(env) - - env['AR'] = 'lib' - env['ARFLAGS'] = SCons.Util.CLVar('/nologo') - env['ARCOM'] = "${TEMPFILE('$AR $ARFLAGS /OUT:$TARGET $SOURCES')}" - env['LIBPREFIX'] = '' - env['LIBSUFFIX'] = '.lib' - -def exists(env): - return msvc_exists() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/mslink.py b/scons/scons-local-2.2.0/SCons/Tool/mslink.py deleted file mode 100644 index 1f53295da..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/mslink.py +++ /dev/null @@ -1,327 +0,0 @@ -"""SCons.Tool.mslink - -Tool-specific initialization for the Microsoft linker. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/mslink.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os.path - -import SCons.Action -import SCons.Defaults -import SCons.Errors -import SCons.Platform.win32 -import SCons.Tool -import SCons.Tool.msvc -import SCons.Tool.msvs -import SCons.Util - -from MSCommon import msvc_setup_env_once, msvc_exists - -def pdbGenerator(env, target, source, for_signature): - try: - return ['/PDB:%s' % target[0].attributes.pdb, '/DEBUG'] - except (AttributeError, IndexError): - return None - -def _dllTargets(target, source, env, for_signature, paramtp): - listCmd = [] - dll = env.FindIxes(target, '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp) - if dll: listCmd.append("/out:%s"%dll.get_string(for_signature)) - - implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX') - if implib: listCmd.append("/implib:%s"%implib.get_string(for_signature)) - - return listCmd - -def _dllSources(target, source, env, for_signature, paramtp): - listCmd = [] - - deffile = env.FindIxes(source, "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX") - for src in source: - # Check explicitly for a non-None deffile so that the __cmp__ - # method of the base SCons.Util.Proxy class used for some Node - # proxies doesn't try to use a non-existent __dict__ attribute. - if deffile and src == deffile: - # Treat this source as a .def file. - listCmd.append("/def:%s" % src.get_string(for_signature)) - else: - # Just treat it as a generic source file. - listCmd.append(src) - return listCmd - -def windowsShlinkTargets(target, source, env, for_signature): - return _dllTargets(target, source, env, for_signature, 'SHLIB') - -def windowsShlinkSources(target, source, env, for_signature): - return _dllSources(target, source, env, for_signature, 'SHLIB') - -def _windowsLdmodTargets(target, source, env, for_signature): - """Get targets for loadable modules.""" - return _dllTargets(target, source, env, for_signature, 'LDMODULE') - -def _windowsLdmodSources(target, source, env, for_signature): - """Get sources for loadable modules.""" - return _dllSources(target, source, env, for_signature, 'LDMODULE') - -def _dllEmitter(target, source, env, paramtp): - """Common implementation of dll emitter.""" - SCons.Tool.msvc.validate_vars(env) - - extratargets = [] - extrasources = [] - - dll = env.FindIxes(target, '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp) - no_import_lib = env.get('no_import_lib', 0) - - if not dll: - raise SCons.Errors.UserError('A shared library should have exactly one target with the suffix: %s' % env.subst('$%sSUFFIX' % paramtp)) - - insert_def = env.subst("$WINDOWS_INSERT_DEF") - if not insert_def in ['', '0', 0] and \ - not env.FindIxes(source, "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX"): - - # append a def file to the list of sources - extrasources.append( - env.ReplaceIxes(dll, - '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp, - "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX")) - - version_num, suite = SCons.Tool.msvs.msvs_parse_version(env.get('MSVS_VERSION', '6.0')) - if version_num >= 8.0 and \ - (env.get('WINDOWS_INSERT_MANIFEST', 0) or env.get('WINDOWS_EMBED_MANIFEST', 0)): - # MSVC 8 and above automatically generate .manifest files that must be installed - extratargets.append( - env.ReplaceIxes(dll, - '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp, - "WINDOWSSHLIBMANIFESTPREFIX", "WINDOWSSHLIBMANIFESTSUFFIX")) - - if 'PDB' in env and env['PDB']: - pdb = env.arg2nodes('$PDB', target=target, source=source)[0] - extratargets.append(pdb) - target[0].attributes.pdb = pdb - - if not no_import_lib and \ - not env.FindIxes(target, "LIBPREFIX", "LIBSUFFIX"): - # Append an import library to the list of targets. - extratargets.append( - env.ReplaceIxes(dll, - '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp, - "LIBPREFIX", "LIBSUFFIX")) - # and .exp file is created if there are exports from a DLL - extratargets.append( - env.ReplaceIxes(dll, - '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp, - "WINDOWSEXPPREFIX", "WINDOWSEXPSUFFIX")) - - return (target+extratargets, source+extrasources) - -def windowsLibEmitter(target, source, env): - return _dllEmitter(target, source, env, 'SHLIB') - -def ldmodEmitter(target, source, env): - """Emitter for loadable modules. - - Loadable modules are identical to shared libraries on Windows, but building - them is subject to different parameters (LDMODULE*). - """ - return _dllEmitter(target, source, env, 'LDMODULE') - -def prog_emitter(target, source, env): - SCons.Tool.msvc.validate_vars(env) - - extratargets = [] - extrasources = [] - - exe = env.FindIxes(target, "PROGPREFIX", "PROGSUFFIX") - if not exe: - raise SCons.Errors.UserError("An executable should have exactly one target with the suffix: %s" % env.subst("$PROGSUFFIX")) - - version_num, suite = SCons.Tool.msvs.msvs_parse_version(env.get('MSVS_VERSION', '6.0')) - if version_num >= 8.0 and \ - (env.get('WINDOWS_INSERT_MANIFEST', 0) or env.get('WINDOWS_EMBED_MANIFEST', 0)): - # MSVC 8 and above automatically generate .manifest files that have to be installed - extratargets.append( - env.ReplaceIxes(exe, - "PROGPREFIX", "PROGSUFFIX", - "WINDOWSPROGMANIFESTPREFIX", "WINDOWSPROGMANIFESTSUFFIX")) - - if 'PDB' in env and env['PDB']: - pdb = env.arg2nodes('$PDB', target=target, source=source)[0] - extratargets.append(pdb) - target[0].attributes.pdb = pdb - - if version_num >= 11.0 and env.get('PCH', 0): - # MSVC 11 and above need the PCH object file to be added to the link line, - # otherwise you get link error LNK2011. - pchobj = SCons.Util.splitext(str(env['PCH']))[0] + '.obj' - # print "prog_emitter, version %s, appending pchobj %s"%(version_num, pchobj) - if pchobj not in extrasources: - extrasources.append(pchobj) - - return (target+extratargets,source+extrasources) - -def RegServerFunc(target, source, env): - if 'register' in env and env['register']: - ret = regServerAction([target[0]], [source[0]], env) - if ret: - raise SCons.Errors.UserError("Unable to register %s" % target[0]) - else: - print "Registered %s sucessfully" % target[0] - return ret - return 0 - -# These are the actual actions run to embed the manifest. -# They are only called from the Check versions below. -embedManifestExeAction = SCons.Action.Action('$MTEXECOM') -embedManifestDllAction = SCons.Action.Action('$MTSHLIBCOM') - -def embedManifestDllCheck(target, source, env): - """Function run by embedManifestDllCheckAction to check for existence of manifest - and other conditions, and embed the manifest by calling embedManifestDllAction if so.""" - if env.get('WINDOWS_EMBED_MANIFEST', 0): - manifestSrc = target[0].abspath + '.manifest' - if os.path.exists(manifestSrc): - ret = (embedManifestDllAction) ([target[0]],None,env) - if ret: - raise SCons.Errors.UserError, "Unable to embed manifest into %s" % (target[0]) - return ret - else: - print '(embed: no %s.manifest found; not embedding.)'%str(target[0]) - return 0 - -def embedManifestExeCheck(target, source, env): - """Function run by embedManifestExeCheckAction to check for existence of manifest - and other conditions, and embed the manifest by calling embedManifestExeAction if so.""" - if env.get('WINDOWS_EMBED_MANIFEST', 0): - manifestSrc = target[0].abspath + '.manifest' - if os.path.exists(manifestSrc): - ret = (embedManifestExeAction) ([target[0]],None,env) - if ret: - raise SCons.Errors.UserError, "Unable to embed manifest into %s" % (target[0]) - return ret - else: - print '(embed: no %s.manifest found; not embedding.)'%str(target[0]) - return 0 - -embedManifestDllCheckAction = SCons.Action.Action(embedManifestDllCheck, None) -embedManifestExeCheckAction = SCons.Action.Action(embedManifestExeCheck, None) - -regServerAction = SCons.Action.Action("$REGSVRCOM", "$REGSVRCOMSTR") -regServerCheck = SCons.Action.Action(RegServerFunc, None) -shlibLinkAction = SCons.Action.Action('${TEMPFILE("$SHLINK $SHLINKFLAGS $_SHLINK_TARGETS $_LIBDIRFLAGS $_LIBFLAGS $_PDB $_SHLINK_SOURCES")}', '$SHLINKCOMSTR') -compositeShLinkAction = shlibLinkAction + regServerCheck + embedManifestDllCheckAction -ldmodLinkAction = SCons.Action.Action('${TEMPFILE("$LDMODULE $LDMODULEFLAGS $_LDMODULE_TARGETS $_LIBDIRFLAGS $_LIBFLAGS $_PDB $_LDMODULE_SOURCES")}', '$LDMODULECOMSTR') -compositeLdmodAction = ldmodLinkAction + regServerCheck + embedManifestDllCheckAction -exeLinkAction = SCons.Action.Action('${TEMPFILE("$LINK $LINKFLAGS /OUT:$TARGET.windows $_LIBDIRFLAGS $_LIBFLAGS $_PDB $SOURCES.windows")}', '$LINKCOMSTR') -compositeLinkAction = exeLinkAction + embedManifestExeCheckAction - -def generate(env): - """Add Builders and construction variables for ar to an Environment.""" - SCons.Tool.createSharedLibBuilder(env) - SCons.Tool.createProgBuilder(env) - - env['SHLINK'] = '$LINK' - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS /dll') - env['_SHLINK_TARGETS'] = windowsShlinkTargets - env['_SHLINK_SOURCES'] = windowsShlinkSources - env['SHLINKCOM'] = compositeShLinkAction - env.Append(SHLIBEMITTER = [windowsLibEmitter]) - env['LINK'] = 'link' - env['LINKFLAGS'] = SCons.Util.CLVar('/nologo') - env['_PDB'] = pdbGenerator - env['LINKCOM'] = compositeLinkAction - env.Append(PROGEMITTER = [prog_emitter]) - env['LIBDIRPREFIX']='/LIBPATH:' - env['LIBDIRSUFFIX']='' - env['LIBLINKPREFIX']='' - env['LIBLINKSUFFIX']='$LIBSUFFIX' - - env['WIN32DEFPREFIX'] = '' - env['WIN32DEFSUFFIX'] = '.def' - env['WIN32_INSERT_DEF'] = 0 - env['WINDOWSDEFPREFIX'] = '${WIN32DEFPREFIX}' - env['WINDOWSDEFSUFFIX'] = '${WIN32DEFSUFFIX}' - env['WINDOWS_INSERT_DEF'] = '${WIN32_INSERT_DEF}' - - env['WIN32EXPPREFIX'] = '' - env['WIN32EXPSUFFIX'] = '.exp' - env['WINDOWSEXPPREFIX'] = '${WIN32EXPPREFIX}' - env['WINDOWSEXPSUFFIX'] = '${WIN32EXPSUFFIX}' - - env['WINDOWSSHLIBMANIFESTPREFIX'] = '' - env['WINDOWSSHLIBMANIFESTSUFFIX'] = '${SHLIBSUFFIX}.manifest' - env['WINDOWSPROGMANIFESTPREFIX'] = '' - env['WINDOWSPROGMANIFESTSUFFIX'] = '${PROGSUFFIX}.manifest' - - env['REGSVRACTION'] = regServerCheck - env['REGSVR'] = os.path.join(SCons.Platform.win32.get_system_root(),'System32','regsvr32') - env['REGSVRFLAGS'] = '/s ' - env['REGSVRCOM'] = '$REGSVR $REGSVRFLAGS ${TARGET.windows}' - - env['WINDOWS_EMBED_MANIFEST'] = 0 - env['MT'] = 'mt' - #env['MTFLAGS'] = ['-hashupdate'] - env['MTFLAGS'] = SCons.Util.CLVar('/nologo') - # Note: use - here to prevent build failure if no manifest produced. - # This seems much simpler than a fancy system using a function action to see - # if the manifest actually exists before trying to run mt with it. - env['MTEXECOM'] = '-$MT $MTFLAGS -manifest ${TARGET}.manifest $_MANIFEST_SOURCES -outputresource:$TARGET;1' - env['MTSHLIBCOM'] = '-$MT $MTFLAGS -manifest ${TARGET}.manifest $_MANIFEST_SOURCES -outputresource:$TARGET;2' - # Future work garyo 27-Feb-11 - env['_MANIFEST_SOURCES'] = None # _windowsManifestSources - - # Set-up ms tools paths - msvc_setup_env_once(env) - - - # Loadable modules are on Windows the same as shared libraries, but they - # are subject to different build parameters (LDMODULE* variables). - # Therefore LDMODULE* variables correspond as much as possible to - # SHLINK*/SHLIB* ones. - SCons.Tool.createLoadableModuleBuilder(env) - env['LDMODULE'] = '$SHLINK' - env['LDMODULEPREFIX'] = '$SHLIBPREFIX' - env['LDMODULESUFFIX'] = '$SHLIBSUFFIX' - env['LDMODULEFLAGS'] = '$SHLINKFLAGS' - env['_LDMODULE_TARGETS'] = _windowsLdmodTargets - env['_LDMODULE_SOURCES'] = _windowsLdmodSources - env['LDMODULEEMITTER'] = [ldmodEmitter] - env['LDMODULECOM'] = compositeLdmodAction - -def exists(env): - return msvc_exists() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/mssdk.py b/scons/scons-local-2.2.0/SCons/Tool/mssdk.py deleted file mode 100644 index f871c7d5a..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/mssdk.py +++ /dev/null @@ -1,50 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/mssdk.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -"""engine.SCons.Tool.mssdk - -Tool-specific initialization for Microsoft SDKs, both Platform -SDKs and Windows SDKs. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -from MSCommon import mssdk_exists, \ - mssdk_setup_env - -def generate(env): - """Add construction variables for an MS SDK to an Environment.""" - mssdk_setup_env(env) - -def exists(env): - return mssdk_exists() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/msvc.py b/scons/scons-local-2.2.0/SCons/Tool/msvc.py deleted file mode 100644 index eb479a35b..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/msvc.py +++ /dev/null @@ -1,278 +0,0 @@ -"""engine.SCons.Tool.msvc - -Tool-specific initialization for Microsoft Visual C/C++. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/msvc.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os.path -import re -import sys - -import SCons.Action -import SCons.Builder -import SCons.Errors -import SCons.Platform.win32 -import SCons.Tool -import SCons.Tool.msvs -import SCons.Util -import SCons.Warnings -import SCons.Scanner.RC - -from MSCommon import msvc_exists, msvc_setup_env_once - -CSuffixes = ['.c', '.C'] -CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++'] - -def validate_vars(env): - """Validate the PCH and PCHSTOP construction variables.""" - if 'PCH' in env and env['PCH']: - if 'PCHSTOP' not in env: - raise SCons.Errors.UserError("The PCHSTOP construction must be defined if PCH is defined.") - if not SCons.Util.is_String(env['PCHSTOP']): - raise SCons.Errors.UserError("The PCHSTOP construction variable must be a string: %r"%env['PCHSTOP']) - -def pch_emitter(target, source, env): - """Adds the object file target.""" - - validate_vars(env) - - pch = None - obj = None - - for t in target: - if SCons.Util.splitext(str(t))[1] == '.pch': - pch = t - if SCons.Util.splitext(str(t))[1] == '.obj': - obj = t - - if not obj: - obj = SCons.Util.splitext(str(pch))[0]+'.obj' - - target = [pch, obj] # pch must be first, and obj second for the PCHCOM to work - - return (target, source) - -def object_emitter(target, source, env, parent_emitter): - """Sets up the PCH dependencies for an object file.""" - - validate_vars(env) - - parent_emitter(target, source, env) - - # Add a dependency, but only if the target (e.g. 'Source1.obj') - # doesn't correspond to the pre-compiled header ('Source1.pch'). - # If the basenames match, then this was most likely caused by - # someone adding the source file to both the env.PCH() and the - # env.Program() calls, and adding the explicit dependency would - # cause a cycle on the .pch file itself. - # - # See issue #2505 for a discussion of what to do if it turns - # out this assumption causes trouble in the wild: - # http://scons.tigris.org/issues/show_bug.cgi?id=2505 - if 'PCH' in env: - pch = env['PCH'] - if str(target[0]) != SCons.Util.splitext(str(pch))[0] + '.obj': - env.Depends(target, pch) - - return (target, source) - -def static_object_emitter(target, source, env): - return object_emitter(target, source, env, - SCons.Defaults.StaticObjectEmitter) - -def shared_object_emitter(target, source, env): - return object_emitter(target, source, env, - SCons.Defaults.SharedObjectEmitter) - -pch_action = SCons.Action.Action('$PCHCOM', '$PCHCOMSTR') -pch_builder = SCons.Builder.Builder(action=pch_action, suffix='.pch', - emitter=pch_emitter, - source_scanner=SCons.Tool.SourceFileScanner) - - -# Logic to build .rc files into .res files (resource files) -res_scanner = SCons.Scanner.RC.RCScan() -res_action = SCons.Action.Action('$RCCOM', '$RCCOMSTR') -res_builder = SCons.Builder.Builder(action=res_action, - src_suffix='.rc', - suffix='.res', - src_builder=[], - source_scanner=res_scanner) - -def msvc_batch_key(action, env, target, source): - """ - Returns a key to identify unique batches of sources for compilation. - - If batching is enabled (via the $MSVC_BATCH setting), then all - target+source pairs that use the same action, defined by the same - environment, and have the same target and source directories, will - be batched. - - Returning None specifies that the specified target+source should not - be batched with other compilations. - """ - - # Fixing MSVC_BATCH mode. Previous if did not work when MSVC_BATCH - # was set to False. This new version should work better. - # Note we need to do the env.subst so $MSVC_BATCH can be a reference to - # another construction variable, which is why we test for False and 0 - # as strings. - if not 'MSVC_BATCH' in env or env.subst('$MSVC_BATCH') in ('0', 'False', '', None): - # We're not using batching; return no key. - return None - t = target[0] - s = source[0] - if os.path.splitext(t.name)[0] != os.path.splitext(s.name)[0]: - # The base names are different, so this *must* be compiled - # separately; return no key. - return None - return (id(action), id(env), t.dir, s.dir) - -def msvc_output_flag(target, source, env, for_signature): - """ - Returns the correct /Fo flag for batching. - - If batching is disabled or there's only one source file, then we - return an /Fo string that specifies the target explicitly. Otherwise, - we return an /Fo string that just specifies the first target's - directory (where the Visual C/C++ compiler will put the .obj files). - """ - - # Fixing MSVC_BATCH mode. Previous if did not work when MSVC_BATCH - # was set to False. This new version should work better. Removed - # len(source)==1 as batch mode can compile only one file - # (and it also fixed problem with compiling only one changed file - # with batch mode enabled) - if not 'MSVC_BATCH' in env or env.subst('$MSVC_BATCH') in ('0', 'False', '', None): - return '/Fo$TARGET' - else: - # The Visual C/C++ compiler requires a \ at the end of the /Fo - # option to indicate an output directory. We use os.sep here so - # that the test(s) for this can be run on non-Windows systems - # without having a hard-coded backslash mess up command-line - # argument parsing. - return '/Fo${TARGET.dir}' + os.sep - -CAction = SCons.Action.Action("$CCCOM", "$CCCOMSTR", - batch_key=msvc_batch_key, - targets='$CHANGED_TARGETS') -ShCAction = SCons.Action.Action("$SHCCCOM", "$SHCCCOMSTR", - batch_key=msvc_batch_key, - targets='$CHANGED_TARGETS') -CXXAction = SCons.Action.Action("$CXXCOM", "$CXXCOMSTR", - batch_key=msvc_batch_key, - targets='$CHANGED_TARGETS') -ShCXXAction = SCons.Action.Action("$SHCXXCOM", "$SHCXXCOMSTR", - batch_key=msvc_batch_key, - targets='$CHANGED_TARGETS') - -def generate(env): - """Add Builders and construction variables for MSVC++ to an Environment.""" - static_obj, shared_obj = SCons.Tool.createObjBuilders(env) - - # TODO(batch): shouldn't reach in to cmdgen this way; necessary - # for now to bypass the checks in Builder.DictCmdGenerator.__call__() - # and allow .cc and .cpp to be compiled in the same command line. - static_obj.cmdgen.source_ext_match = False - shared_obj.cmdgen.source_ext_match = False - - for suffix in CSuffixes: - static_obj.add_action(suffix, CAction) - shared_obj.add_action(suffix, ShCAction) - static_obj.add_emitter(suffix, static_object_emitter) - shared_obj.add_emitter(suffix, shared_object_emitter) - - for suffix in CXXSuffixes: - static_obj.add_action(suffix, CXXAction) - shared_obj.add_action(suffix, ShCXXAction) - static_obj.add_emitter(suffix, static_object_emitter) - shared_obj.add_emitter(suffix, shared_object_emitter) - - env['CCPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Z7") or ""}']) - env['CCPCHFLAGS'] = SCons.Util.CLVar(['${(PCH and "/Yu%s \\\"/Fp%s\\\""%(PCHSTOP or "",File(PCH))) or ""}']) - env['_MSVC_OUTPUT_FLAG'] = msvc_output_flag - env['_CCCOMCOM'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $CCPCHFLAGS $CCPDBFLAGS' - env['CC'] = 'cl' - env['CCFLAGS'] = SCons.Util.CLVar('/nologo') - env['CFLAGS'] = SCons.Util.CLVar('') - env['CCCOM'] = '${TEMPFILE("$CC $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CFLAGS $CCFLAGS $_CCCOMCOM")}' - env['SHCC'] = '$CC' - env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') - env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS') - env['SHCCCOM'] = '${TEMPFILE("$SHCC $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCFLAGS $SHCCFLAGS $_CCCOMCOM")}' - env['CXX'] = '$CC' - env['CXXFLAGS'] = SCons.Util.CLVar('$( /TP $)') - env['CXXCOM'] = '${TEMPFILE("$CXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CXXFLAGS $CCFLAGS $_CCCOMCOM")}' - env['SHCXX'] = '$CXX' - env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') - env['SHCXXCOM'] = '${TEMPFILE("$SHCXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM")}' - env['CPPDEFPREFIX'] = '/D' - env['CPPDEFSUFFIX'] = '' - env['INCPREFIX'] = '/I' - env['INCSUFFIX'] = '' -# env.Append(OBJEMITTER = [static_object_emitter]) -# env.Append(SHOBJEMITTER = [shared_object_emitter]) - env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 - - env['RC'] = 'rc' - env['RCFLAGS'] = SCons.Util.CLVar('') - env['RCSUFFIXES']=['.rc','.rc2'] - env['RCCOM'] = '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES' - env['BUILDERS']['RES'] = res_builder - env['OBJPREFIX'] = '' - env['OBJSUFFIX'] = '.obj' - env['SHOBJPREFIX'] = '$OBJPREFIX' - env['SHOBJSUFFIX'] = '$OBJSUFFIX' - - # Set-up ms tools paths - msvc_setup_env_once(env) - - env['CFILESUFFIX'] = '.c' - env['CXXFILESUFFIX'] = '.cc' - - env['PCHPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Yd") or ""}']) - env['PCHCOM'] = '$CXX /Fo${TARGETS[1]} $CXXFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS $PCHPDBFLAGS' - env['BUILDERS']['PCH'] = pch_builder - - if 'ENV' not in env: - env['ENV'] = {} - if 'SystemRoot' not in env['ENV']: # required for dlls in the winsxs folders - env['ENV']['SystemRoot'] = SCons.Platform.win32.get_system_root() - -def exists(env): - return msvc_exists() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/msvs.py b/scons/scons-local-2.2.0/SCons/Tool/msvs.py deleted file mode 100644 index c0443d96d..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/msvs.py +++ /dev/null @@ -1,1799 +0,0 @@ -"""SCons.Tool.msvs - -Tool-specific initialization for Microsoft Visual Studio project files. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Tool/msvs.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.compat - -import base64 -import hashlib -import ntpath -import os -# compat layer imports "cPickle" for us if it's available. -import pickle -import re -import sys - -import SCons.Builder -import SCons.Node.FS -import SCons.Platform.win32 -import SCons.Script.SConscript -import SCons.PathList -import SCons.Util -import SCons.Warnings - -from MSCommon import msvc_exists, msvc_setup_env_once -from SCons.Defaults import processDefines - -############################################################################## -# Below here are the classes and functions for generation of -# DSP/DSW/SLN/VCPROJ files. -############################################################################## - -def xmlify(s): - s = s.replace("&", "&") # do this first - s = s.replace("'", "'") - s = s.replace('"', """) - return s - -# Process a CPPPATH list in includes, given the env, target and source. -# Returns a tuple of nodes. -def processIncludes(includes, env, target, source): - return SCons.PathList.PathList(includes).subst_path(env, target, source) - - -external_makefile_guid = '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}' - -def _generateGUID(slnfile, name): - """This generates a dummy GUID for the sln file to use. It is - based on the MD5 signatures of the sln filename plus the name of - the project. It basically just needs to be unique, and not - change with each invocation.""" - m = hashlib.md5() - # Normalize the slnfile path to a Windows path (\ separators) so - # the generated file has a consistent GUID even if we generate - # it on a non-Windows platform. - m.update(ntpath.normpath(str(slnfile)) + str(name)) - solution = m.hexdigest().upper() - # convert most of the signature to GUID form (discard the rest) - solution = "{" + solution[:8] + "-" + solution[8:12] + "-" + solution[12:16] + "-" + solution[16:20] + "-" + solution[20:32] + "}" - return solution - -version_re = re.compile(r'(\d+\.\d+)(.*)') - -def msvs_parse_version(s): - """ - Split a Visual Studio version, which may in fact be something like - '7.0Exp', into is version number (returned as a float) and trailing - "suite" portion. - """ - num, suite = version_re.match(s).groups() - return float(num), suite - -# os.path.relpath has been introduced in Python 2.6 -# We define it locally for earlier versions of Python -def relpath(path, start=os.path.curdir): - """Return a relative version of a path""" - import sys - if not path: - raise ValueError("no path specified") - start_list = os.path.abspath(start).split(os.sep) - path_list = os.path.abspath(path).split(os.sep) - if 'posix' in sys.builtin_module_names: - # Work out how much of the filepath is shared by start and path. - i = len(os.path.commonprefix([start_list, path_list])) - else: - if start_list[0].lower() != path_list[0].lower(): - unc_path, rest = os.path.splitunc(path) - unc_start, rest = os.path.splitunc(start) - if bool(unc_path) ^ bool(unc_start): - raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)" - % (path, start)) - else: - raise ValueError("path is on drive %s, start on drive %s" - % (path_list[0], start_list[0])) - # Work out how much of the filepath is shared by start and path. - for i in range(min(len(start_list), len(path_list))): - if start_list[i].lower() != path_list[i].lower(): - break - else: - i += 1 - rel_list = [os.pardir] * (len(start_list)-i) + path_list[i:] - if not rel_list: - return os.path.curdir - return os.path.join(*rel_list) - -if not "relpath" in os.path.__all__: - os.path.relpath = relpath - -# This is how we re-invoke SCons from inside MSVS Project files. -# The problem is that we might have been invoked as either scons.bat -# or scons.py. If we were invoked directly as scons.py, then we could -# use sys.argv[0] to find the SCons "executable," but that doesn't work -# if we were invoked as scons.bat, which uses "python -c" to execute -# things and ends up with "-c" as sys.argv[0]. Consequently, we have -# the MSVS Project file invoke SCons the same way that scons.bat does, -# which works regardless of how we were invoked. -def getExecScriptMain(env, xml=None): - scons_home = env.get('SCONS_HOME') - if not scons_home and 'SCONS_LIB_DIR' in os.environ: - scons_home = os.environ['SCONS_LIB_DIR'] - if scons_home: - exec_script_main = "from os.path import join; import sys; sys.path = [ r'%s' ] + sys.path; import SCons.Script; SCons.Script.main()" % scons_home - else: - version = SCons.__version__ - exec_script_main = "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-%(version)s'), join(sys.prefix, 'scons-%(version)s'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons') ] + sys.path; import SCons.Script; SCons.Script.main()" % locals() - if xml: - exec_script_main = xmlify(exec_script_main) - return exec_script_main - -# The string for the Python executable we tell the Project file to use -# is either sys.executable or, if an external PYTHON_ROOT environment -# variable exists, $(PYTHON)ROOT\\python.exe (generalized a little to -# pluck the actual executable name from sys.executable). -try: - python_root = os.environ['PYTHON_ROOT'] -except KeyError: - python_executable = sys.executable -else: - python_executable = os.path.join('$$(PYTHON_ROOT)', - os.path.split(sys.executable)[1]) - -class Config(object): - pass - -def splitFully(path): - dir, base = os.path.split(path) - if dir and dir != '' and dir != path: - return splitFully(dir)+[base] - if base == '': - return [] - return [base] - -def makeHierarchy(sources): - '''Break a list of files into a hierarchy; for each value, if it is a string, - then it is a file. If it is a dictionary, it is a folder. The string is - the original path of the file.''' - - hierarchy = {} - for file in sources: - path = splitFully(file) - if len(path): - dict = hierarchy - for part in path[:-1]: - if part not in dict: - dict[part] = {} - dict = dict[part] - dict[path[-1]] = file - #else: - # print 'Warning: failed to decompose path for '+str(file) - return hierarchy - -class _DSPGenerator(object): - """ Base class for DSP generators """ - - srcargs = [ - 'srcs', - 'incs', - 'localincs', - 'resources', - 'misc'] - - def __init__(self, dspfile, source, env): - self.dspfile = str(dspfile) - try: - get_abspath = dspfile.get_abspath - except AttributeError: - self.dspabs = os.path.abspath(dspfile) - else: - self.dspabs = get_abspath() - - if 'variant' not in env: - raise SCons.Errors.InternalError("You must specify a 'variant' argument (i.e. 'Debug' or " +\ - "'Release') to create an MSVSProject.") - elif SCons.Util.is_String(env['variant']): - variants = [env['variant']] - elif SCons.Util.is_List(env['variant']): - variants = env['variant'] - - if 'buildtarget' not in env or env['buildtarget'] == None: - buildtarget = [''] - elif SCons.Util.is_String(env['buildtarget']): - buildtarget = [env['buildtarget']] - elif SCons.Util.is_List(env['buildtarget']): - if len(env['buildtarget']) != len(variants): - raise SCons.Errors.InternalError("Sizes of 'buildtarget' and 'variant' lists must be the same.") - buildtarget = [] - for bt in env['buildtarget']: - if SCons.Util.is_String(bt): - buildtarget.append(bt) - else: - buildtarget.append(bt.get_abspath()) - else: - buildtarget = [env['buildtarget'].get_abspath()] - if len(buildtarget) == 1: - bt = buildtarget[0] - buildtarget = [] - for _ in variants: - buildtarget.append(bt) - - if 'outdir' not in env or env['outdir'] == None: - outdir = [''] - elif SCons.Util.is_String(env['outdir']): - outdir = [env['outdir']] - elif SCons.Util.is_List(env['outdir']): - if len(env['outdir']) != len(variants): - raise SCons.Errors.InternalError("Sizes of 'outdir' and 'variant' lists must be the same.") - outdir = [] - for s in env['outdir']: - if SCons.Util.is_String(s): - outdir.append(s) - else: - outdir.append(s.get_abspath()) - else: - outdir = [env['outdir'].get_abspath()] - if len(outdir) == 1: - s = outdir[0] - outdir = [] - for v in variants: - outdir.append(s) - - if 'runfile' not in env or env['runfile'] == None: - runfile = buildtarget[-1:] - elif SCons.Util.is_String(env['runfile']): - runfile = [env['runfile']] - elif SCons.Util.is_List(env['runfile']): - if len(env['runfile']) != len(variants): - raise SCons.Errors.InternalError("Sizes of 'runfile' and 'variant' lists must be the same.") - runfile = [] - for s in env['runfile']: - if SCons.Util.is_String(s): - runfile.append(s) - else: - runfile.append(s.get_abspath()) - else: - runfile = [env['runfile'].get_abspath()] - if len(runfile) == 1: - s = runfile[0] - runfile = [] - for v in variants: - runfile.append(s) - - self.sconscript = env['MSVSSCONSCRIPT'] - - cmdargs = env.get('cmdargs', '') - - self.env = env - - if 'name' in self.env: - self.name = self.env['name'] - else: - self.name = os.path.basename(SCons.Util.splitext(self.dspfile)[0]) - self.name = self.env.subst(self.name) - - sourcenames = [ - 'Source Files', - 'Header Files', - 'Local Headers', - 'Resource Files', - 'Other Files'] - - self.sources = {} - for n in sourcenames: - self.sources[n] = [] - - self.configs = {} - - self.nokeep = 0 - if 'nokeep' in env and env['variant'] != 0: - self.nokeep = 1 - - if self.nokeep == 0 and os.path.exists(self.dspabs): - self.Parse() - - for t in zip(sourcenames,self.srcargs): - if t[1] in self.env: - if SCons.Util.is_List(self.env[t[1]]): - for i in self.env[t[1]]: - if not i in self.sources[t[0]]: - self.sources[t[0]].append(i) - else: - if not self.env[t[1]] in self.sources[t[0]]: - self.sources[t[0]].append(self.env[t[1]]) - - for n in sourcenames: - #TODO 2.4: compat layer supports sorted(key=) but not sort(key=) - #TODO 2.4: self.sources[n].sort(key=lambda a: a.lower()) - self.sources[n] = sorted(self.sources[n], key=lambda a: a.lower()) - - def AddConfig(self, variant, buildtarget, outdir, runfile, cmdargs, dspfile=dspfile): - config = Config() - config.buildtarget = buildtarget - config.outdir = outdir - config.cmdargs = cmdargs - config.runfile = runfile - - match = re.match('(.*)\|(.*)', variant) - if match: - config.variant = match.group(1) - config.platform = match.group(2) - else: - config.variant = variant - config.platform = 'Win32' - - self.configs[variant] = config - print "Adding '" + self.name + ' - ' + config.variant + '|' + config.platform + "' to '" + str(dspfile) + "'" - - for i in range(len(variants)): - AddConfig(self, variants[i], buildtarget[i], outdir[i], runfile[i], cmdargs) - - self.platforms = [] - for key in self.configs.keys(): - platform = self.configs[key].platform - if not platform in self.platforms: - self.platforms.append(platform) - - def Build(self): - pass - -V6DSPHeader = """\ -# Microsoft Developer Studio Project File - Name="%(name)s" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) External Target" 0x0106 - -CFG=%(name)s - Win32 %(confkey)s -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "%(name)s.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "%(name)s.mak" CFG="%(name)s - Win32 %(confkey)s" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -""" - -class _GenerateV6DSP(_DSPGenerator): - """Generates a Project file for MSVS 6.0""" - - def PrintHeader(self): - # pick a default config - confkeys = sorted(self.configs.keys()) - - name = self.name - confkey = confkeys[0] - - self.file.write(V6DSPHeader % locals()) - - for kind in confkeys: - self.file.write('!MESSAGE "%s - Win32 %s" (based on "Win32 (x86) External Target")\n' % (name, kind)) - - self.file.write('!MESSAGE \n\n') - - def PrintProject(self): - name = self.name - self.file.write('# Begin Project\n' - '# PROP AllowPerConfigDependencies 0\n' - '# PROP Scc_ProjName ""\n' - '# PROP Scc_LocalPath ""\n\n') - - first = 1 - confkeys = sorted(self.configs.keys()) - for kind in confkeys: - outdir = self.configs[kind].outdir - buildtarget = self.configs[kind].buildtarget - if first == 1: - self.file.write('!IF "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind)) - first = 0 - else: - self.file.write('\n!ELSEIF "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind)) - - env_has_buildtarget = 'MSVSBUILDTARGET' in self.env - if not env_has_buildtarget: - self.env['MSVSBUILDTARGET'] = buildtarget - - # have to write this twice, once with the BASE settings, and once without - for base in ("BASE ",""): - self.file.write('# PROP %sUse_MFC 0\n' - '# PROP %sUse_Debug_Libraries ' % (base, base)) - if kind.lower().find('debug') < 0: - self.file.write('0\n') - else: - self.file.write('1\n') - self.file.write('# PROP %sOutput_Dir "%s"\n' - '# PROP %sIntermediate_Dir "%s"\n' % (base,outdir,base,outdir)) - cmd = 'echo Starting SCons && ' + self.env.subst('$MSVSBUILDCOM', 1) - self.file.write('# PROP %sCmd_Line "%s"\n' - '# PROP %sRebuild_Opt "-c && %s"\n' - '# PROP %sTarget_File "%s"\n' - '# PROP %sBsc_Name ""\n' - '# PROP %sTarget_Dir ""\n'\ - %(base,cmd,base,cmd,base,buildtarget,base,base)) - - if not env_has_buildtarget: - del self.env['MSVSBUILDTARGET'] - - self.file.write('\n!ENDIF\n\n' - '# Begin Target\n\n') - for kind in confkeys: - self.file.write('# Name "%s - Win32 %s"\n' % (name,kind)) - self.file.write('\n') - first = 0 - for kind in confkeys: - if first == 0: - self.file.write('!IF "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind)) - first = 1 - else: - self.file.write('!ELSEIF "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind)) - self.file.write('!ENDIF \n\n') - self.PrintSourceFiles() - self.file.write('# End Target\n' - '# End Project\n') - - if self.nokeep == 0: - # now we pickle some data and add it to the file -- MSDEV will ignore it. - pdata = pickle.dumps(self.configs,1) - pdata = base64.encodestring(pdata) - self.file.write(pdata + '\n') - pdata = pickle.dumps(self.sources,1) - pdata = base64.encodestring(pdata) - self.file.write(pdata + '\n') - - def PrintSourceFiles(self): - categories = {'Source Files': 'cpp|c|cxx|l|y|def|odl|idl|hpj|bat', - 'Header Files': 'h|hpp|hxx|hm|inl', - 'Local Headers': 'h|hpp|hxx|hm|inl', - 'Resource Files': 'r|rc|ico|cur|bmp|dlg|rc2|rct|bin|cnt|rtf|gif|jpg|jpeg|jpe', - 'Other Files': ''} - - for kind in sorted(categories.keys(), key=lambda a: a.lower()): - if not self.sources[kind]: - continue # skip empty groups - - self.file.write('# Begin Group "' + kind + '"\n\n') - typelist = categories[kind].replace('|', ';') - self.file.write('# PROP Default_Filter "' + typelist + '"\n') - - for file in self.sources[kind]: - file = os.path.normpath(file) - self.file.write('# Begin Source File\n\n' - 'SOURCE="' + file + '"\n' - '# End Source File\n') - self.file.write('# End Group\n') - - # add the SConscript file outside of the groups - self.file.write('# Begin Source File\n\n' - 'SOURCE="' + str(self.sconscript) + '"\n' - '# End Source File\n') - - def Parse(self): - try: - dspfile = open(self.dspabs,'r') - except IOError: - return # doesn't exist yet, so can't add anything to configs. - - line = dspfile.readline() - while line: - if line.find("# End Project") > -1: - break - line = dspfile.readline() - - line = dspfile.readline() - datas = line - while line and line != '\n': - line = dspfile.readline() - datas = datas + line - - # OK, we've found our little pickled cache of data. - try: - datas = base64.decodestring(datas) - data = pickle.loads(datas) - except KeyboardInterrupt: - raise - except: - return # unable to unpickle any data for some reason - - self.configs.update(data) - - data = None - line = dspfile.readline() - datas = line - while line and line != '\n': - line = dspfile.readline() - datas = datas + line - - # OK, we've found our little pickled cache of data. - # it has a "# " in front of it, so we strip that. - try: - datas = base64.decodestring(datas) - data = pickle.loads(datas) - except KeyboardInterrupt: - raise - except: - return # unable to unpickle any data for some reason - - self.sources.update(data) - - def Build(self): - try: - self.file = open(self.dspabs,'w') - except IOError, detail: - raise SCons.Errors.InternalError('Unable to open "' + self.dspabs + '" for writing:' + str(detail)) - else: - self.PrintHeader() - self.PrintProject() - self.file.close() - -V7DSPHeader = """\ - - -""" - -V7DSPConfiguration = """\ -\t\t -\t\t\t -\t\t -""" - -V8DSPHeader = """\ - - -""" - -V8DSPConfiguration = """\ -\t\t -\t\t\t -\t\t -""" -class _GenerateV7DSP(_DSPGenerator): - """Generates a Project file for MSVS .NET""" - - def __init__(self, dspfile, source, env): - _DSPGenerator.__init__(self, dspfile, source, env) - self.version = env['MSVS_VERSION'] - self.version_num, self.suite = msvs_parse_version(self.version) - if self.version_num >= 9.0: - self.versionstr = '9.00' - self.dspheader = V8DSPHeader - self.dspconfiguration = V8DSPConfiguration - elif self.version_num >= 8.0: - self.versionstr = '8.00' - self.dspheader = V8DSPHeader - self.dspconfiguration = V8DSPConfiguration - else: - if self.version_num >= 7.1: - self.versionstr = '7.10' - else: - self.versionstr = '7.00' - self.dspheader = V7DSPHeader - self.dspconfiguration = V7DSPConfiguration - self.file = None - - def PrintHeader(self): - env = self.env - versionstr = self.versionstr - name = self.name - encoding = self.env.subst('$MSVSENCODING') - scc_provider = env.get('MSVS_SCC_PROVIDER', '') - scc_project_name = env.get('MSVS_SCC_PROJECT_NAME', '') - scc_aux_path = env.get('MSVS_SCC_AUX_PATH', '') - # MSVS_SCC_LOCAL_PATH is kept for backwards compatibility purpose and should - # be deprecated as soon as possible. - scc_local_path_legacy = env.get('MSVS_SCC_LOCAL_PATH', '') - scc_connection_root = env.get('MSVS_SCC_CONNECTION_ROOT', os.curdir) - scc_local_path = os.path.relpath(scc_connection_root, os.path.dirname(self.dspabs)) - project_guid = env.get('MSVS_PROJECT_GUID', '') - if not project_guid: - project_guid = _generateGUID(self.dspfile, '') - if scc_provider != '': - scc_attrs = '\tSccProjectName="%s"\n' % scc_project_name - if scc_aux_path != '': - scc_attrs += '\tSccAuxPath="%s"\n' % scc_aux_path - scc_attrs += ('\tSccLocalPath="%s"\n' - '\tSccProvider="%s"' % (scc_local_path, scc_provider)) - elif scc_local_path_legacy != '': - # This case is kept for backwards compatibility purpose and should - # be deprecated as soon as possible. - scc_attrs = ('\tSccProjectName="%s"\n' - '\tSccLocalPath="%s"' % (scc_project_name, scc_local_path_legacy)) - else: - self.dspheader = self.dspheader.replace('%(scc_attrs)s\n', '') - - self.file.write(self.dspheader % locals()) - - self.file.write('\t\n') - for platform in self.platforms: - self.file.write( - '\t\t\n' % platform) - self.file.write('\t\n') - - if self.version_num >= 8.0: - self.file.write('\t\n' - '\t\n') - - def PrintProject(self): - self.file.write('\t\n') - - confkeys = sorted(self.configs.keys()) - for kind in confkeys: - variant = self.configs[kind].variant - platform = self.configs[kind].platform - outdir = self.configs[kind].outdir - buildtarget = self.configs[kind].buildtarget - runfile = self.configs[kind].runfile - cmdargs = self.configs[kind].cmdargs - - env_has_buildtarget = 'MSVSBUILDTARGET' in self.env - if not env_has_buildtarget: - self.env['MSVSBUILDTARGET'] = buildtarget - - starting = 'echo Starting SCons && ' - if cmdargs: - cmdargs = ' ' + cmdargs - else: - cmdargs = '' - buildcmd = xmlify(starting + self.env.subst('$MSVSBUILDCOM', 1) + cmdargs) - rebuildcmd = xmlify(starting + self.env.subst('$MSVSREBUILDCOM', 1) + cmdargs) - cleancmd = xmlify(starting + self.env.subst('$MSVSCLEANCOM', 1) + cmdargs) - - # This isn't perfect; CPPDEFINES and CPPPATH can contain $TARGET and $SOURCE, - # so they could vary depending on the command being generated. This code - # assumes they don't. - preprocdefs = xmlify(';'.join(processDefines(self.env.get('CPPDEFINES', [])))) - includepath_Dirs = processIncludes(self.env.get('CPPPATH', []), self.env, None, None) - includepath = xmlify(';'.join([str(x) for x in includepath_Dirs])) - - if not env_has_buildtarget: - del self.env['MSVSBUILDTARGET'] - - self.file.write(self.dspconfiguration % locals()) - - self.file.write('\t\n') - - if self.version_num >= 7.1: - self.file.write('\t\n' - '\t\n') - - self.PrintSourceFiles() - - self.file.write('\n') - - if self.nokeep == 0: - # now we pickle some data and add it to the file -- MSDEV will ignore it. - pdata = pickle.dumps(self.configs,1) - pdata = base64.encodestring(pdata) - self.file.write('\n') - - def printSources(self, hierarchy, commonprefix): - sorteditems = sorted(hierarchy.items(), key=lambda a: a[0].lower()) - - # First folders, then files - for key, value in sorteditems: - if SCons.Util.is_Dict(value): - self.file.write('\t\t\t\n' % (key)) - self.printSources(value, commonprefix) - self.file.write('\t\t\t\n') - - for key, value in sorteditems: - if SCons.Util.is_String(value): - file = value - if commonprefix: - file = os.path.join(commonprefix, value) - file = os.path.normpath(file) - self.file.write('\t\t\t\n' - '\t\t\t\n' % (file)) - - def PrintSourceFiles(self): - categories = {'Source Files': 'cpp;c;cxx;l;y;def;odl;idl;hpj;bat', - 'Header Files': 'h;hpp;hxx;hm;inl', - 'Local Headers': 'h;hpp;hxx;hm;inl', - 'Resource Files': 'r;rc;ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe', - 'Other Files': ''} - - self.file.write('\t\n') - - cats = sorted([k for k in categories.keys() if self.sources[k]], - key=lambda a: a.lower()) - for kind in cats: - if len(cats) > 1: - self.file.write('\t\t\n' % (kind, categories[kind])) - - sources = self.sources[kind] - - # First remove any common prefix - commonprefix = None - s = list(map(os.path.normpath, sources)) - # take the dirname because the prefix may include parts - # of the filenames (e.g. if you have 'dir\abcd' and - # 'dir\acde' then the cp will be 'dir\a' ) - cp = os.path.dirname( os.path.commonprefix(s) ) - if cp and s[0][len(cp)] == os.sep: - # +1 because the filename starts after the separator - sources = [s[len(cp)+1:] for s in sources] - commonprefix = cp - - hierarchy = makeHierarchy(sources) - self.printSources(hierarchy, commonprefix=commonprefix) - - if len(cats)>1: - self.file.write('\t\t\n') - - # add the SConscript file outside of the groups - self.file.write('\t\t\n' - '\t\t\n' % str(self.sconscript)) - - self.file.write('\t\n' - '\t\n' - '\t\n') - - def Parse(self): - try: - dspfile = open(self.dspabs,'r') - except IOError: - return # doesn't exist yet, so can't add anything to configs. - - line = dspfile.readline() - while line: - if line.find('\n') - - def printFilters(self, hierarchy, name): - sorteditems = sorted(hierarchy.items(), key = lambda a: a[0].lower()) - - for key, value in sorteditems: - if SCons.Util.is_Dict(value): - filter_name = name + '\\' + key - self.filters_file.write('\t\t\n' - '\t\t\t%s\n' - '\t\t\n' % (filter_name, _generateGUID(self.dspabs, filter_name))) - self.printFilters(value, filter_name) - - def printSources(self, hierarchy, kind, commonprefix, filter_name): - keywords = {'Source Files': 'ClCompile', - 'Header Files': 'ClInclude', - 'Local Headers': 'ClInclude', - 'Resource Files': 'None', - 'Other Files': 'None'} - - sorteditems = sorted(hierarchy.items(), key = lambda a: a[0].lower()) - - # First folders, then files - for key, value in sorteditems: - if SCons.Util.is_Dict(value): - self.printSources(value, kind, commonprefix, filter_name + '\\' + key) - - for key, value in sorteditems: - if SCons.Util.is_String(value): - file = value - if commonprefix: - file = os.path.join(commonprefix, value) - file = os.path.normpath(file) - - self.file.write('\t\t<%s Include="%s" />\n' % (keywords[kind], file)) - self.filters_file.write('\t\t<%s Include="%s">\n' - '\t\t\t%s\n' - '\t\t\n' % (keywords[kind], file, filter_name, keywords[kind])) - - def PrintSourceFiles(self): - categories = {'Source Files': 'cpp;c;cxx;l;y;def;odl;idl;hpj;bat', - 'Header Files': 'h;hpp;hxx;hm;inl', - 'Local Headers': 'h;hpp;hxx;hm;inl', - 'Resource Files': 'r;rc;ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe', - 'Other Files': ''} - - cats = sorted([k for k in categories.keys() if self.sources[k]], - key = lambda a: a.lower()) - - # print vcxproj.filters file first - self.filters_file.write('\t\n') - for kind in cats: - self.filters_file.write('\t\t\n' - '\t\t\t{7b42d31d-d53c-4868-8b92-ca2bc9fc052f}\n' - '\t\t\t%s\n' - '\t\t\n' % (kind, categories[kind])) - - # First remove any common prefix - sources = self.sources[kind] - commonprefix = None - s = list(map(os.path.normpath, sources)) - # take the dirname because the prefix may include parts - # of the filenames (e.g. if you have 'dir\abcd' and - # 'dir\acde' then the cp will be 'dir\a' ) - cp = os.path.dirname( os.path.commonprefix(s) ) - if cp and s[0][len(cp)] == os.sep: - # +1 because the filename starts after the separator - sources = [s[len(cp)+1:] for s in sources] - commonprefix = cp - - hierarchy = makeHierarchy(sources) - self.printFilters(hierarchy, kind) - - self.filters_file.write('\t\n') - - # then print files and filters - for kind in cats: - self.file.write('\t\n') - self.filters_file.write('\t\n') - - # First remove any common prefix - sources = self.sources[kind] - commonprefix = None - s = list(map(os.path.normpath, sources)) - # take the dirname because the prefix may include parts - # of the filenames (e.g. if you have 'dir\abcd' and - # 'dir\acde' then the cp will be 'dir\a' ) - cp = os.path.dirname( os.path.commonprefix(s) ) - if cp and s[0][len(cp)] == os.sep: - # +1 because the filename starts after the separator - sources = [s[len(cp)+1:] for s in sources] - commonprefix = cp - - hierarchy = makeHierarchy(sources) - self.printSources(hierarchy, kind, commonprefix, kind) - - self.file.write('\t\n') - self.filters_file.write('\t\n') - - # add the SConscript file outside of the groups - self.file.write('\t\n' - '\t\t\n' - #'\t\t\n' - '\t\n' % str(self.sconscript)) - - def Parse(self): - print "_GenerateV10DSP.Parse()" - - def Build(self): - try: - self.file = open(self.dspabs, 'w') - except IOError, detail: - raise SCons.Errors.InternalError('Unable to open "' + self.dspabs + '" for writing:' + str(detail)) - else: - self.PrintHeader() - self.PrintProject() - self.file.close() - -class _DSWGenerator(object): - """ Base class for DSW generators """ - def __init__(self, dswfile, source, env): - self.dswfile = os.path.normpath(str(dswfile)) - self.dsw_folder_path = os.path.dirname(os.path.abspath(self.dswfile)) - self.env = env - - if 'projects' not in env: - raise SCons.Errors.UserError("You must specify a 'projects' argument to create an MSVSSolution.") - projects = env['projects'] - if not SCons.Util.is_List(projects): - raise SCons.Errors.InternalError("The 'projects' argument must be a list of nodes.") - projects = SCons.Util.flatten(projects) - if len(projects) < 1: - raise SCons.Errors.UserError("You must specify at least one project to create an MSVSSolution.") - self.dspfiles = list(map(str, projects)) - - if 'name' in self.env: - self.name = self.env['name'] - else: - self.name = os.path.basename(SCons.Util.splitext(self.dswfile)[0]) - self.name = self.env.subst(self.name) - - def Build(self): - pass - -class _GenerateV7DSW(_DSWGenerator): - """Generates a Solution file for MSVS .NET""" - def __init__(self, dswfile, source, env): - _DSWGenerator.__init__(self, dswfile, source, env) - - self.file = None - self.version = self.env['MSVS_VERSION'] - self.version_num, self.suite = msvs_parse_version(self.version) - self.versionstr = '7.00' - if self.version_num >= 11.0: - self.versionstr = '12.0' - elif self.version_num >= 10.0: - self.versionstr = '11.00' - elif self.version_num >= 9.0: - self.versionstr = '10.00' - elif self.version_num >= 8.0: - self.versionstr = '9.00' - elif self.version_num >= 7.1: - self.versionstr = '8.00' - - if 'slnguid' in env and env['slnguid']: - self.slnguid = env['slnguid'] - else: - self.slnguid = _generateGUID(dswfile, self.name) - - self.configs = {} - - self.nokeep = 0 - if 'nokeep' in env and env['variant'] != 0: - self.nokeep = 1 - - if self.nokeep == 0 and os.path.exists(self.dswfile): - self.Parse() - - def AddConfig(self, variant, dswfile=dswfile): - config = Config() - - match = re.match('(.*)\|(.*)', variant) - if match: - config.variant = match.group(1) - config.platform = match.group(2) - else: - config.variant = variant - config.platform = 'Win32' - - self.configs[variant] = config - print "Adding '" + self.name + ' - ' + config.variant + '|' + config.platform + "' to '" + str(dswfile) + "'" - - if 'variant' not in env: - raise SCons.Errors.InternalError("You must specify a 'variant' argument (i.e. 'Debug' or " +\ - "'Release') to create an MSVS Solution File.") - elif SCons.Util.is_String(env['variant']): - AddConfig(self, env['variant']) - elif SCons.Util.is_List(env['variant']): - for variant in env['variant']: - AddConfig(self, variant) - - self.platforms = [] - for key in self.configs.keys(): - platform = self.configs[key].platform - if not platform in self.platforms: - self.platforms.append(platform) - - def GenerateProjectFilesInfo(self): - for dspfile in self.dspfiles: - dsp_folder_path, name = os.path.split(dspfile) - dsp_folder_path = os.path.abspath(dsp_folder_path) - dsp_relative_folder_path = os.path.relpath(dsp_folder_path, self.dsw_folder_path) - if dsp_relative_folder_path == os.curdir: - dsp_relative_file_path = name - else: - dsp_relative_file_path = os.path.join(dsp_relative_folder_path, name) - dspfile_info = {'NAME': name, - 'GUID': _generateGUID(dspfile, ''), - 'FOLDER_PATH': dsp_folder_path, - 'FILE_PATH': dspfile, - 'SLN_RELATIVE_FOLDER_PATH': dsp_relative_folder_path, - 'SLN_RELATIVE_FILE_PATH': dsp_relative_file_path} - self.dspfiles_info.append(dspfile_info) - - self.dspfiles_info = [] - GenerateProjectFilesInfo(self) - - def Parse(self): - try: - dswfile = open(self.dswfile,'r') - except IOError: - return # doesn't exist yet, so can't add anything to configs. - - line = dswfile.readline() - while line: - if line[:9] == "EndGlobal": - break - line = dswfile.readline() - - line = dswfile.readline() - datas = line - while line: - line = dswfile.readline() - datas = datas + line - - # OK, we've found our little pickled cache of data. - try: - datas = base64.decodestring(datas) - data = pickle.loads(datas) - except KeyboardInterrupt: - raise - except: - return # unable to unpickle any data for some reason - - self.configs.update(data) - - def PrintSolution(self): - """Writes a solution file""" - self.file.write('Microsoft Visual Studio Solution File, Format Version %s\n' % self.versionstr) - if self.versionstr >= 11.0: - self.file.write('# Visual Studio 11\n') - elif self.version_num >= 10.0: - self.file.write('# Visual Studio 2010\n') - elif self.version_num >= 9.0: - self.file.write('# Visual Studio 2008\n') - elif self.version_num >= 8.0: - self.file.write('# Visual Studio 2005\n') - - for dspinfo in self.dspfiles_info: - name = dspinfo['NAME'] - base, suffix = SCons.Util.splitext(name) - if suffix == '.vcproj': - name = base - self.file.write('Project("%s") = "%s", "%s", "%s"\n' - % (external_makefile_guid, name, dspinfo['SLN_RELATIVE_FILE_PATH'], dspinfo['GUID'])) - if self.version_num >= 7.1 and self.version_num < 8.0: - self.file.write('\tProjectSection(ProjectDependencies) = postProject\n' - '\tEndProjectSection\n') - self.file.write('EndProject\n') - - self.file.write('Global\n') - - env = self.env - if 'MSVS_SCC_PROVIDER' in env: - scc_number_of_projects = len(self.dspfiles) + 1 - slnguid = self.slnguid - scc_provider = env.get('MSVS_SCC_PROVIDER', '').replace(' ', r'\u0020') - scc_project_name = env.get('MSVS_SCC_PROJECT_NAME', '').replace(' ', r'\u0020') - scc_connection_root = env.get('MSVS_SCC_CONNECTION_ROOT', os.curdir) - scc_local_path = os.path.relpath(scc_connection_root, self.dsw_folder_path).replace('\\', '\\\\') - self.file.write('\tGlobalSection(SourceCodeControl) = preSolution\n' - '\t\tSccNumberOfProjects = %(scc_number_of_projects)d\n' - '\t\tSccProjectName0 = %(scc_project_name)s\n' - '\t\tSccLocalPath0 = %(scc_local_path)s\n' - '\t\tSccProvider0 = %(scc_provider)s\n' - '\t\tCanCheckoutShared = true\n' % locals()) - sln_relative_path_from_scc = os.path.relpath(self.dsw_folder_path, scc_connection_root) - if sln_relative_path_from_scc != os.curdir: - self.file.write('\t\tSccProjectFilePathRelativizedFromConnection0 = %s\\\\\n' - % sln_relative_path_from_scc.replace('\\', '\\\\')) - if self.version_num < 8.0: - # When present, SolutionUniqueID is automatically removed by VS 2005 - # TODO: check for Visual Studio versions newer than 2005 - self.file.write('\t\tSolutionUniqueID = %s\n' % slnguid) - for dspinfo in self.dspfiles_info: - i = self.dspfiles_info.index(dspinfo) + 1 - dsp_relative_file_path = dspinfo['SLN_RELATIVE_FILE_PATH'].replace('\\', '\\\\') - dsp_scc_relative_folder_path = os.path.relpath(dspinfo['FOLDER_PATH'], scc_connection_root).replace('\\', '\\\\') - self.file.write('\t\tSccProjectUniqueName%(i)s = %(dsp_relative_file_path)s\n' - '\t\tSccLocalPath%(i)d = %(scc_local_path)s\n' - '\t\tCanCheckoutShared = true\n' - '\t\tSccProjectFilePathRelativizedFromConnection%(i)s = %(dsp_scc_relative_folder_path)s\\\\\n' - % locals()) - self.file.write('\tEndGlobalSection\n') - if self.version_num >= 8.0: - self.file.write('\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n') - else: - self.file.write('\tGlobalSection(SolutionConfiguration) = preSolution\n') - - confkeys = sorted(self.configs.keys()) - cnt = 0 - for name in confkeys: - variant = self.configs[name].variant - platform = self.configs[name].platform - if self.version_num >= 8.0: - self.file.write('\t\t%s|%s = %s|%s\n' % (variant, platform, variant, platform)) - else: - self.file.write('\t\tConfigName.%d = %s\n' % (cnt, variant)) - cnt = cnt + 1 - self.file.write('\tEndGlobalSection\n') - if self.version_num <= 7.1: - self.file.write('\tGlobalSection(ProjectDependencies) = postSolution\n' - '\tEndGlobalSection\n') - if self.version_num >= 8.0: - self.file.write('\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n') - else: - self.file.write('\tGlobalSection(ProjectConfiguration) = postSolution\n') - - for name in confkeys: - variant = self.configs[name].variant - platform = self.configs[name].platform - if self.version_num >= 8.0: - for dspinfo in self.dspfiles_info: - guid = dspinfo['GUID'] - self.file.write('\t\t%s.%s|%s.ActiveCfg = %s|%s\n' - '\t\t%s.%s|%s.Build.0 = %s|%s\n' % (guid,variant,platform,variant,platform,guid,variant,platform,variant,platform)) - else: - for dspinfo in self.dspfiles_info: - guid = dspinfo['GUID'] - self.file.write('\t\t%s.%s.ActiveCfg = %s|%s\n' - '\t\t%s.%s.Build.0 = %s|%s\n' %(guid,variant,variant,platform,guid,variant,variant,platform)) - - self.file.write('\tEndGlobalSection\n') - - if self.version_num >= 8.0: - self.file.write('\tGlobalSection(SolutionProperties) = preSolution\n' - '\t\tHideSolutionNode = FALSE\n' - '\tEndGlobalSection\n') - else: - self.file.write('\tGlobalSection(ExtensibilityGlobals) = postSolution\n' - '\tEndGlobalSection\n' - '\tGlobalSection(ExtensibilityAddIns) = postSolution\n' - '\tEndGlobalSection\n') - self.file.write('EndGlobal\n') - if self.nokeep == 0: - pdata = pickle.dumps(self.configs,1) - pdata = base64.encodestring(pdata) - self.file.write(pdata + '\n') - - def Build(self): - try: - self.file = open(self.dswfile,'w') - except IOError, detail: - raise SCons.Errors.InternalError('Unable to open "' + self.dswfile + '" for writing:' + str(detail)) - else: - self.PrintSolution() - self.file.close() - -V6DSWHeader = """\ -Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! - -############################################################################### - -Project: "%(name)s"="%(dspfile)s" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Global: - -Package=<5> -{{{ -}}} - -Package=<3> -{{{ -}}} - -############################################################################### -""" - -class _GenerateV6DSW(_DSWGenerator): - """Generates a Workspace file for MSVS 6.0""" - - def PrintWorkspace(self): - """ writes a DSW file """ - name = self.name - dspfile = os.path.relpath(self.dspfiles[0], self.dsw_folder_path) - self.file.write(V6DSWHeader % locals()) - - def Build(self): - try: - self.file = open(self.dswfile,'w') - except IOError, detail: - raise SCons.Errors.InternalError('Unable to open "' + self.dswfile + '" for writing:' + str(detail)) - else: - self.PrintWorkspace() - self.file.close() - - -def GenerateDSP(dspfile, source, env): - """Generates a Project file based on the version of MSVS that is being used""" - - version_num = 6.0 - if 'MSVS_VERSION' in env: - version_num, suite = msvs_parse_version(env['MSVS_VERSION']) - if version_num >= 10.0: - g = _GenerateV10DSP(dspfile, source, env) - g.Build() - elif version_num >= 7.0: - g = _GenerateV7DSP(dspfile, source, env) - g.Build() - else: - g = _GenerateV6DSP(dspfile, source, env) - g.Build() - -def GenerateDSW(dswfile, source, env): - """Generates a Solution/Workspace file based on the version of MSVS that is being used""" - - version_num = 6.0 - if 'MSVS_VERSION' in env: - version_num, suite = msvs_parse_version(env['MSVS_VERSION']) - if version_num >= 7.0: - g = _GenerateV7DSW(dswfile, source, env) - g.Build() - else: - g = _GenerateV6DSW(dswfile, source, env) - g.Build() - - -############################################################################## -# Above here are the classes and functions for generation of -# DSP/DSW/SLN/VCPROJ files. -############################################################################## - -def GetMSVSProjectSuffix(target, source, env, for_signature): - return env['MSVS']['PROJECTSUFFIX'] - -def GetMSVSSolutionSuffix(target, source, env, for_signature): - return env['MSVS']['SOLUTIONSUFFIX'] - -def GenerateProject(target, source, env): - # generate the dsp file, according to the version of MSVS. - builddspfile = target[0] - dspfile = builddspfile.srcnode() - - # this detects whether or not we're using a VariantDir - if not dspfile is builddspfile: - try: - bdsp = open(str(builddspfile), "w+") - except IOError, detail: - print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n' - raise - - bdsp.write("This is just a placeholder file.\nThe real project file is here:\n%s\n" % dspfile.get_abspath()) - - GenerateDSP(dspfile, source, env) - - if env.get('auto_build_solution', 1): - builddswfile = target[1] - dswfile = builddswfile.srcnode() - - if not dswfile is builddswfile: - - try: - bdsw = open(str(builddswfile), "w+") - except IOError, detail: - print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n' - raise - - bdsw.write("This is just a placeholder file.\nThe real workspace file is here:\n%s\n" % dswfile.get_abspath()) - - GenerateDSW(dswfile, source, env) - -def GenerateSolution(target, source, env): - GenerateDSW(target[0], source, env) - -def projectEmitter(target, source, env): - """Sets up the DSP dependencies.""" - - # todo: Not sure what sets source to what user has passed as target, - # but this is what happens. When that is fixed, we also won't have - # to make the user always append env['MSVSPROJECTSUFFIX'] to target. - if source[0] == target[0]: - source = [] - - # make sure the suffix is correct for the version of MSVS we're running. - (base, suff) = SCons.Util.splitext(str(target[0])) - suff = env.subst('$MSVSPROJECTSUFFIX') - target[0] = base + suff - - if not source: - source = 'prj_inputs:' - source = source + env.subst('$MSVSSCONSCOM', 1) - source = source + env.subst('$MSVSENCODING', 1) - - # Project file depends on CPPDEFINES and CPPPATH - preprocdefs = xmlify(';'.join(processDefines(env.get('CPPDEFINES', [])))) - includepath_Dirs = processIncludes(env.get('CPPPATH', []), env, None, None) - includepath = xmlify(';'.join([str(x) for x in includepath_Dirs])) - source = source + "; ppdefs:%s incpath:%s"%(preprocdefs, includepath) - - if 'buildtarget' in env and env['buildtarget'] != None: - if SCons.Util.is_String(env['buildtarget']): - source = source + ' "%s"' % env['buildtarget'] - elif SCons.Util.is_List(env['buildtarget']): - for bt in env['buildtarget']: - if SCons.Util.is_String(bt): - source = source + ' "%s"' % bt - else: - try: source = source + ' "%s"' % bt.get_abspath() - except AttributeError: raise SCons.Errors.InternalError("buildtarget can be a string, a node, a list of strings or nodes, or None") - else: - try: source = source + ' "%s"' % env['buildtarget'].get_abspath() - except AttributeError: raise SCons.Errors.InternalError("buildtarget can be a string, a node, a list of strings or nodes, or None") - - if 'outdir' in env and env['outdir'] != None: - if SCons.Util.is_String(env['outdir']): - source = source + ' "%s"' % env['outdir'] - elif SCons.Util.is_List(env['outdir']): - for s in env['outdir']: - if SCons.Util.is_String(s): - source = source + ' "%s"' % s - else: - try: source = source + ' "%s"' % s.get_abspath() - except AttributeError: raise SCons.Errors.InternalError("outdir can be a string, a node, a list of strings or nodes, or None") - else: - try: source = source + ' "%s"' % env['outdir'].get_abspath() - except AttributeError: raise SCons.Errors.InternalError("outdir can be a string, a node, a list of strings or nodes, or None") - - if 'name' in env: - if SCons.Util.is_String(env['name']): - source = source + ' "%s"' % env['name'] - else: - raise SCons.Errors.InternalError("name must be a string") - - if 'variant' in env: - if SCons.Util.is_String(env['variant']): - source = source + ' "%s"' % env['variant'] - elif SCons.Util.is_List(env['variant']): - for variant in env['variant']: - if SCons.Util.is_String(variant): - source = source + ' "%s"' % variant - else: - raise SCons.Errors.InternalError("name must be a string or a list of strings") - else: - raise SCons.Errors.InternalError("variant must be a string or a list of strings") - else: - raise SCons.Errors.InternalError("variant must be specified") - - for s in _DSPGenerator.srcargs: - if s in env: - if SCons.Util.is_String(env[s]): - source = source + ' "%s' % env[s] - elif SCons.Util.is_List(env[s]): - for t in env[s]: - if SCons.Util.is_String(t): - source = source + ' "%s"' % t - else: - raise SCons.Errors.InternalError(s + " must be a string or a list of strings") - else: - raise SCons.Errors.InternalError(s + " must be a string or a list of strings") - - source = source + ' "%s"' % str(target[0]) - source = [SCons.Node.Python.Value(source)] - - targetlist = [target[0]] - sourcelist = source - - if env.get('auto_build_solution', 1): - env['projects'] = [env.File(t).srcnode() for t in targetlist] - t, s = solutionEmitter(target, target, env) - targetlist = targetlist + t - - return (targetlist, sourcelist) - -def solutionEmitter(target, source, env): - """Sets up the DSW dependencies.""" - - # todo: Not sure what sets source to what user has passed as target, - # but this is what happens. When that is fixed, we also won't have - # to make the user always append env['MSVSSOLUTIONSUFFIX'] to target. - if source[0] == target[0]: - source = [] - - # make sure the suffix is correct for the version of MSVS we're running. - (base, suff) = SCons.Util.splitext(str(target[0])) - suff = env.subst('$MSVSSOLUTIONSUFFIX') - target[0] = base + suff - - if not source: - source = 'sln_inputs:' - - if 'name' in env: - if SCons.Util.is_String(env['name']): - source = source + ' "%s"' % env['name'] - else: - raise SCons.Errors.InternalError("name must be a string") - - if 'variant' in env: - if SCons.Util.is_String(env['variant']): - source = source + ' "%s"' % env['variant'] - elif SCons.Util.is_List(env['variant']): - for variant in env['variant']: - if SCons.Util.is_String(variant): - source = source + ' "%s"' % variant - else: - raise SCons.Errors.InternalError("name must be a string or a list of strings") - else: - raise SCons.Errors.InternalError("variant must be a string or a list of strings") - else: - raise SCons.Errors.InternalError("variant must be specified") - - if 'slnguid' in env: - if SCons.Util.is_String(env['slnguid']): - source = source + ' "%s"' % env['slnguid'] - else: - raise SCons.Errors.InternalError("slnguid must be a string") - - if 'projects' in env: - if SCons.Util.is_String(env['projects']): - source = source + ' "%s"' % env['projects'] - elif SCons.Util.is_List(env['projects']): - for t in env['projects']: - if SCons.Util.is_String(t): - source = source + ' "%s"' % t - - source = source + ' "%s"' % str(target[0]) - source = [SCons.Node.Python.Value(source)] - - return ([target[0]], source) - -projectAction = SCons.Action.Action(GenerateProject, None) - -solutionAction = SCons.Action.Action(GenerateSolution, None) - -projectBuilder = SCons.Builder.Builder(action = '$MSVSPROJECTCOM', - suffix = '$MSVSPROJECTSUFFIX', - emitter = projectEmitter) - -solutionBuilder = SCons.Builder.Builder(action = '$MSVSSOLUTIONCOM', - suffix = '$MSVSSOLUTIONSUFFIX', - emitter = solutionEmitter) - -default_MSVS_SConscript = None - -def generate(env): - """Add Builders and construction variables for Microsoft Visual - Studio project files to an Environment.""" - try: - env['BUILDERS']['MSVSProject'] - except KeyError: - env['BUILDERS']['MSVSProject'] = projectBuilder - - try: - env['BUILDERS']['MSVSSolution'] - except KeyError: - env['BUILDERS']['MSVSSolution'] = solutionBuilder - - env['MSVSPROJECTCOM'] = projectAction - env['MSVSSOLUTIONCOM'] = solutionAction - - if SCons.Script.call_stack: - # XXX Need to find a way to abstract this; the build engine - # shouldn't depend on anything in SCons.Script. - env['MSVSSCONSCRIPT'] = SCons.Script.call_stack[0].sconscript - else: - global default_MSVS_SConscript - if default_MSVS_SConscript is None: - default_MSVS_SConscript = env.File('SConstruct') - env['MSVSSCONSCRIPT'] = default_MSVS_SConscript - - env['MSVSSCONS'] = '"%s" -c "%s"' % (python_executable, getExecScriptMain(env)) - env['MSVSSCONSFLAGS'] = '-C "${MSVSSCONSCRIPT.dir.abspath}" -f ${MSVSSCONSCRIPT.name}' - env['MSVSSCONSCOM'] = '$MSVSSCONS $MSVSSCONSFLAGS' - env['MSVSBUILDCOM'] = '$MSVSSCONSCOM "$MSVSBUILDTARGET"' - env['MSVSREBUILDCOM'] = '$MSVSSCONSCOM "$MSVSBUILDTARGET"' - env['MSVSCLEANCOM'] = '$MSVSSCONSCOM -c "$MSVSBUILDTARGET"' - - # Set-up ms tools paths for default version - msvc_setup_env_once(env) - - if 'MSVS_VERSION' in env: - version_num, suite = msvs_parse_version(env['MSVS_VERSION']) - else: - (version_num, suite) = (7.0, None) # guess at a default - if 'MSVS' not in env: - env['MSVS'] = {} - if (version_num < 7.0): - env['MSVS']['PROJECTSUFFIX'] = '.dsp' - env['MSVS']['SOLUTIONSUFFIX'] = '.dsw' - elif (version_num < 10.0): - env['MSVS']['PROJECTSUFFIX'] = '.vcproj' - env['MSVS']['SOLUTIONSUFFIX'] = '.sln' - else: - env['MSVS']['PROJECTSUFFIX'] = '.vcxproj' - env['MSVS']['SOLUTIONSUFFIX'] = '.sln' - - if (version_num >= 10.0): - env['MSVSENCODING'] = 'utf-8' - else: - env['MSVSENCODING'] = 'Windows-1252' - - env['GET_MSVSPROJECTSUFFIX'] = GetMSVSProjectSuffix - env['GET_MSVSSOLUTIONSUFFIX'] = GetMSVSSolutionSuffix - env['MSVSPROJECTSUFFIX'] = '${GET_MSVSPROJECTSUFFIX}' - env['MSVSSOLUTIONSUFFIX'] = '${GET_MSVSSOLUTIONSUFFIX}' - env['SCONS_HOME'] = os.environ.get('SCONS_HOME') - -def exists(env): - return msvc_exists() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/mwcc.py b/scons/scons-local-2.2.0/SCons/Tool/mwcc.py deleted file mode 100644 index 689c2739a..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/mwcc.py +++ /dev/null @@ -1,207 +0,0 @@ -"""SCons.Tool.mwcc - -Tool-specific initialization for the Metrowerks CodeWarrior compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/mwcc.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os -import os.path - -import SCons.Util - -def set_vars(env): - """Set MWCW_VERSION, MWCW_VERSIONS, and some codewarrior environment vars - - MWCW_VERSIONS is set to a list of objects representing installed versions - - MWCW_VERSION is set to the version object that will be used for building. - MWCW_VERSION can be set to a string during Environment - construction to influence which version is chosen, otherwise - the latest one from MWCW_VERSIONS is used. - - Returns true if at least one version is found, false otherwise - """ - desired = env.get('MWCW_VERSION', '') - - # return right away if the variables are already set - if isinstance(desired, MWVersion): - return 1 - elif desired is None: - return 0 - - versions = find_versions() - version = None - - if desired: - for v in versions: - if str(v) == desired: - version = v - elif versions: - version = versions[-1] - - env['MWCW_VERSIONS'] = versions - env['MWCW_VERSION'] = version - - if version is None: - return 0 - - env.PrependENVPath('PATH', version.clpath) - env.PrependENVPath('PATH', version.dllpath) - ENV = env['ENV'] - ENV['CWFolder'] = version.path - ENV['LM_LICENSE_FILE'] = version.license - plus = lambda x: '+%s' % x - ENV['MWCIncludes'] = os.pathsep.join(map(plus, version.includes)) - ENV['MWLibraries'] = os.pathsep.join(map(plus, version.libs)) - return 1 - - -def find_versions(): - """Return a list of MWVersion objects representing installed versions""" - versions = [] - - ### This function finds CodeWarrior by reading from the registry on - ### Windows. Some other method needs to be implemented for other - ### platforms, maybe something that calls env.WhereIs('mwcc') - - if SCons.Util.can_read_reg: - try: - HLM = SCons.Util.HKEY_LOCAL_MACHINE - product = 'SOFTWARE\\Metrowerks\\CodeWarrior\\Product Versions' - product_key = SCons.Util.RegOpenKeyEx(HLM, product) - - i = 0 - while True: - name = product + '\\' + SCons.Util.RegEnumKey(product_key, i) - name_key = SCons.Util.RegOpenKeyEx(HLM, name) - - try: - version = SCons.Util.RegQueryValueEx(name_key, 'VERSION') - path = SCons.Util.RegQueryValueEx(name_key, 'PATH') - mwv = MWVersion(version[0], path[0], 'Win32-X86') - versions.append(mwv) - except SCons.Util.RegError: - pass - - i = i + 1 - - except SCons.Util.RegError: - pass - - return versions - - -class MWVersion(object): - def __init__(self, version, path, platform): - self.version = version - self.path = path - self.platform = platform - self.clpath = os.path.join(path, 'Other Metrowerks Tools', - 'Command Line Tools') - self.dllpath = os.path.join(path, 'Bin') - - # The Metrowerks tools don't store any configuration data so they - # are totally dumb when it comes to locating standard headers, - # libraries, and other files, expecting all the information - # to be handed to them in environment variables. The members set - # below control what information scons injects into the environment - - ### The paths below give a normal build environment in CodeWarrior for - ### Windows, other versions of CodeWarrior might need different paths. - - msl = os.path.join(path, 'MSL') - support = os.path.join(path, '%s Support' % platform) - - self.license = os.path.join(path, 'license.dat') - self.includes = [msl, support] - self.libs = [msl, support] - - def __str__(self): - return self.version - - -CSuffixes = ['.c', '.C'] -CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++'] - - -def generate(env): - """Add Builders and construction variables for the mwcc to an Environment.""" - import SCons.Defaults - import SCons.Tool - - set_vars(env) - - static_obj, shared_obj = SCons.Tool.createObjBuilders(env) - - for suffix in CSuffixes: - static_obj.add_action(suffix, SCons.Defaults.CAction) - shared_obj.add_action(suffix, SCons.Defaults.ShCAction) - - for suffix in CXXSuffixes: - static_obj.add_action(suffix, SCons.Defaults.CXXAction) - shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction) - - env['CCCOMFLAGS'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -nolink -o $TARGET $SOURCES' - - env['CC'] = 'mwcc' - env['CCCOM'] = '$CC $CFLAGS $CCFLAGS $CCCOMFLAGS' - - env['CXX'] = 'mwcc' - env['CXXCOM'] = '$CXX $CXXFLAGS $CCCOMFLAGS' - - env['SHCC'] = '$CC' - env['SHCCFLAGS'] = '$CCFLAGS' - env['SHCFLAGS'] = '$CFLAGS' - env['SHCCCOM'] = '$SHCC $SHCFLAGS $SHCCFLAGS $CCCOMFLAGS' - - env['SHCXX'] = '$CXX' - env['SHCXXFLAGS'] = '$CXXFLAGS' - env['SHCXXCOM'] = '$SHCXX $SHCXXFLAGS $CCCOMFLAGS' - - env['CFILESUFFIX'] = '.c' - env['CXXFILESUFFIX'] = '.cpp' - env['CPPDEFPREFIX'] = '-D' - env['CPPDEFSUFFIX'] = '' - env['INCPREFIX'] = '-I' - env['INCSUFFIX'] = '' - - #env['PCH'] = ? - #env['PCHSTOP'] = ? - - -def exists(env): - return set_vars(env) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/mwld.py b/scons/scons-local-2.2.0/SCons/Tool/mwld.py deleted file mode 100644 index 30149c37e..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/mwld.py +++ /dev/null @@ -1,107 +0,0 @@ -"""SCons.Tool.mwld - -Tool-specific initialization for the Metrowerks CodeWarrior linker. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/mwld.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Tool - - -def generate(env): - """Add Builders and construction variables for lib to an Environment.""" - SCons.Tool.createStaticLibBuilder(env) - SCons.Tool.createSharedLibBuilder(env) - SCons.Tool.createProgBuilder(env) - - env['AR'] = 'mwld' - env['ARCOM'] = '$AR $ARFLAGS -library -o $TARGET $SOURCES' - - env['LIBDIRPREFIX'] = '-L' - env['LIBDIRSUFFIX'] = '' - env['LIBLINKPREFIX'] = '-l' - env['LIBLINKSUFFIX'] = '.lib' - - env['LINK'] = 'mwld' - env['LINKCOM'] = '$LINK $LINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' - - env['SHLINK'] = '$LINK' - env['SHLINKFLAGS'] = '$LINKFLAGS' - env['SHLINKCOM'] = shlib_action - env['SHLIBEMITTER']= shlib_emitter - - -def exists(env): - import SCons.Tool.mwcc - return SCons.Tool.mwcc.set_vars(env) - - -def shlib_generator(target, source, env, for_signature): - cmd = ['$SHLINK', '$SHLINKFLAGS', '-shared'] - - no_import_lib = env.get('no_import_lib', 0) - if no_import_lib: cmd.extend('-noimplib') - - dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') - if dll: cmd.extend(['-o', dll]) - - implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX') - if implib: cmd.extend(['-implib', implib.get_string(for_signature)]) - - cmd.extend(['$SOURCES', '$_LIBDIRFLAGS', '$_LIBFLAGS']) - - return [cmd] - - -def shlib_emitter(target, source, env): - dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') - no_import_lib = env.get('no_import_lib', 0) - - if not dll: - raise SCons.Errors.UserError("A shared library should have exactly one target with the suffix: %s" % env.subst("$SHLIBSUFFIX")) - - if not no_import_lib and \ - not env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX'): - - # Append an import library to the list of targets. - target.append(env.ReplaceIxes(dll, - 'SHLIBPREFIX', 'SHLIBSUFFIX', - 'LIBPREFIX', 'LIBSUFFIX')) - - return target, source - - -shlib_action = SCons.Action.Action(shlib_generator, generator=1) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/nasm.py b/scons/scons-local-2.2.0/SCons/Tool/nasm.py deleted file mode 100644 index e76b51ab1..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/nasm.py +++ /dev/null @@ -1,72 +0,0 @@ -"""SCons.Tool.nasm - -Tool-specific initialization for nasm, the famous Netwide Assembler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/nasm.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Defaults -import SCons.Tool -import SCons.Util - -ASSuffixes = ['.s', '.asm', '.ASM'] -ASPPSuffixes = ['.spp', '.SPP', '.sx'] -if SCons.Util.case_sensitive_suffixes('.s', '.S'): - ASPPSuffixes.extend(['.S']) -else: - ASSuffixes.extend(['.S']) - -def generate(env): - """Add Builders and construction variables for nasm to an Environment.""" - static_obj, shared_obj = SCons.Tool.createObjBuilders(env) - - for suffix in ASSuffixes: - static_obj.add_action(suffix, SCons.Defaults.ASAction) - static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) - - for suffix in ASPPSuffixes: - static_obj.add_action(suffix, SCons.Defaults.ASPPAction) - static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) - - env['AS'] = 'nasm' - env['ASFLAGS'] = SCons.Util.CLVar('') - env['ASPPFLAGS'] = '$ASFLAGS' - env['ASCOM'] = '$AS $ASFLAGS -o $TARGET $SOURCES' - env['ASPPCOM'] = '$CC $ASPPFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES' - -def exists(env): - return env.Detect('nasm') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/packaging/__init__.py b/scons/scons-local-2.2.0/SCons/Tool/packaging/__init__.py deleted file mode 100644 index f2b953d67..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/packaging/__init__.py +++ /dev/null @@ -1,312 +0,0 @@ -"""SCons.Tool.Packaging - -SCons Packaging Tool. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Tool/packaging/__init__.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Environment -from SCons.Variables import * -from SCons.Errors import * -from SCons.Util import is_List, make_path_relative -from SCons.Warnings import warn, Warning - -import os, imp -import SCons.Defaults - -__all__ = [ 'src_targz', 'src_tarbz2', 'src_zip', 'tarbz2', 'targz', 'zip', 'rpm', 'msi', 'ipk' ] - -# -# Utility and Builder function -# -def Tag(env, target, source, *more_tags, **kw_tags): - """ Tag a file with the given arguments, just sets the accordingly named - attribute on the file object. - - TODO: FIXME - """ - if not target: - target=source - first_tag=None - else: - first_tag=source - - if first_tag: - kw_tags[first_tag[0]] = '' - - if len(kw_tags) == 0 and len(more_tags) == 0: - raise UserError("No tags given.") - - # XXX: sanity checks - for x in more_tags: - kw_tags[x] = '' - - if not SCons.Util.is_List(target): - target=[target] - else: - # hmm, sometimes the target list, is a list of a list - # make sure it is flattened prior to processing. - # TODO: perhaps some bug ?!? - target=env.Flatten(target) - - for t in target: - for (k,v) in kw_tags.items(): - # all file tags have to start with PACKAGING_, so we can later - # differentiate between "normal" object attributes and the - # packaging attributes. As the user should not be bothered with - # that, the prefix will be added here if missing. - #if not k.startswith('PACKAGING_'): - if k[:10] != 'PACKAGING_': - k='PACKAGING_'+k - setattr(t, k, v) - -def Package(env, target=None, source=None, **kw): - """ Entry point for the package tool. - """ - # check if we need to find the source files ourself - if not source: - source = env.FindInstalledFiles() - - if len(source)==0: - raise UserError("No source for Package() given") - - # decide which types of packages shall be built. Can be defined through - # four mechanisms: command line argument, keyword argument, - # environment argument and default selection( zip or tar.gz ) in that - # order. - try: kw['PACKAGETYPE']=env['PACKAGETYPE'] - except KeyError: pass - - if not kw.get('PACKAGETYPE'): - from SCons.Script import GetOption - kw['PACKAGETYPE'] = GetOption('package_type') - - if kw['PACKAGETYPE'] == None: - if 'Tar' in env['BUILDERS']: - kw['PACKAGETYPE']='targz' - elif 'Zip' in env['BUILDERS']: - kw['PACKAGETYPE']='zip' - else: - raise UserError("No type for Package() given") - - PACKAGETYPE=kw['PACKAGETYPE'] - if not is_List(PACKAGETYPE): - PACKAGETYPE=PACKAGETYPE.split(',') - - # load the needed packagers. - def load_packager(type): - try: - file,path,desc=imp.find_module(type, __path__) - return imp.load_module(type, file, path, desc) - except ImportError, e: - raise EnvironmentError("packager %s not available: %s"%(type,str(e))) - - packagers=list(map(load_packager, PACKAGETYPE)) - - # set up targets and the PACKAGEROOT - try: - # fill up the target list with a default target name until the PACKAGETYPE - # list is of the same size as the target list. - if not target: target = [] - - size_diff = len(PACKAGETYPE)-len(target) - default_name = "%(NAME)s-%(VERSION)s" - - if size_diff>0: - default_target = default_name%kw - target.extend( [default_target]*size_diff ) - - if 'PACKAGEROOT' not in kw: - kw['PACKAGEROOT'] = default_name%kw - - except KeyError, e: - raise SCons.Errors.UserError( "Missing Packagetag '%s'"%e.args[0] ) - - # setup the source files - source=env.arg2nodes(source, env.fs.Entry) - - # call the packager to setup the dependencies. - targets=[] - try: - for packager in packagers: - t=[target.pop(0)] - t=packager.package(env,t,source, **kw) - targets.extend(t) - - assert( len(target) == 0 ) - - except KeyError, e: - raise SCons.Errors.UserError( "Missing Packagetag '%s' for %s packager"\ - % (e.args[0],packager.__name__) ) - except TypeError, e: - # this exception means that a needed argument for the packager is - # missing. As our packagers get their "tags" as named function - # arguments we need to find out which one is missing. - from inspect import getargspec - args,varargs,varkw,defaults=getargspec(packager.package) - if defaults!=None: - args=args[:-len(defaults)] # throw away arguments with default values - args.remove('env') - args.remove('target') - args.remove('source') - # now remove any args for which we have a value in kw. - args=[x for x in args if x not in kw] - - if len(args)==0: - raise # must be a different error, so reraise - elif len(args)==1: - raise SCons.Errors.UserError( "Missing Packagetag '%s' for %s packager"\ - % (args[0],packager.__name__) ) - else: - raise SCons.Errors.UserError( "Missing Packagetags '%s' for %s packager"\ - % (", ".join(args),packager.__name__) ) - - target=env.arg2nodes(target, env.fs.Entry) - targets.extend(env.Alias( 'package', targets )) - return targets - -# -# SCons tool initialization functions -# - -added = None - -def generate(env): - from SCons.Script import AddOption - global added - if not added: - added = 1 - AddOption('--package-type', - dest='package_type', - default=None, - type="string", - action="store", - help='The type of package to create.') - - try: - env['BUILDERS']['Package'] - env['BUILDERS']['Tag'] - except KeyError: - env['BUILDERS']['Package'] = Package - env['BUILDERS']['Tag'] = Tag - -def exists(env): - return 1 - -# XXX -def options(opts): - opts.AddVariables( - EnumVariable( 'PACKAGETYPE', - 'the type of package to create.', - None, allowed_values=list(map( str, __all__ )), - ignorecase=2 - ) - ) - -# -# Internal utility functions -# - -def copy_attr(f1, f2): - """ copies the special packaging file attributes from f1 to f2. - """ - #pattrs = [x for x in dir(f1) if not hasattr(f2, x) and\ - # x.startswith('PACKAGING_')] - copyit = lambda x: not hasattr(f2, x) and x[:10] == 'PACKAGING_' - pattrs = list(filter(copyit, dir(f1))) - for attr in pattrs: - setattr(f2, attr, getattr(f1, attr)) -def putintopackageroot(target, source, env, pkgroot, honor_install_location=1): - """ Uses the CopyAs builder to copy all source files to the directory given - in pkgroot. - - If honor_install_location is set and the copied source file has an - PACKAGING_INSTALL_LOCATION attribute, the PACKAGING_INSTALL_LOCATION is - used as the new name of the source file under pkgroot. - - The source file will not be copied if it is already under the the pkgroot - directory. - - All attributes of the source file will be copied to the new file. - """ - # make sure the packageroot is a Dir object. - if SCons.Util.is_String(pkgroot): pkgroot=env.Dir(pkgroot) - if not SCons.Util.is_List(source): source=[source] - - new_source = [] - for file in source: - if SCons.Util.is_String(file): file = env.File(file) - - if file.is_under(pkgroot): - new_source.append(file) - else: - if hasattr(file, 'PACKAGING_INSTALL_LOCATION') and\ - honor_install_location: - new_name=make_path_relative(file.PACKAGING_INSTALL_LOCATION) - else: - new_name=make_path_relative(file.get_path()) - - new_file=pkgroot.File(new_name) - new_file=env.CopyAs(new_file, file)[0] - copy_attr(file, new_file) - new_source.append(new_file) - - return (target, new_source) - -def stripinstallbuilder(target, source, env): - """ strips the install builder action from the source list and stores - the final installation location as the "PACKAGING_INSTALL_LOCATION" of - the source of the source file. This effectively removes the final installed - files from the source list while remembering the installation location. - - It also warns about files which have no install builder attached. - """ - def has_no_install_location(file): - return not (file.has_builder() and\ - hasattr(file.builder, 'name') and\ - (file.builder.name=="InstallBuilder" or\ - file.builder.name=="InstallAsBuilder")) - - if len(list(filter(has_no_install_location, source))): - warn(Warning, "there are files to package which have no\ - InstallBuilder attached, this might lead to irreproducible packages") - - n_source=[] - for s in source: - if has_no_install_location(s): - n_source.append(s) - else: - for ss in s.sources: - n_source.append(ss) - copy_attr(s, ss) - setattr(ss, 'PACKAGING_INSTALL_LOCATION', s.get_path()) - - return (target, n_source) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/packaging/ipk.py b/scons/scons-local-2.2.0/SCons/Tool/packaging/ipk.py deleted file mode 100644 index 251de8a33..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/packaging/ipk.py +++ /dev/null @@ -1,185 +0,0 @@ -"""SCons.Tool.Packaging.ipk -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/packaging/ipk.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Builder -import SCons.Node.FS -import os - -from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot - -def package(env, target, source, PACKAGEROOT, NAME, VERSION, DESCRIPTION, - SUMMARY, X_IPK_PRIORITY, X_IPK_SECTION, SOURCE_URL, - X_IPK_MAINTAINER, X_IPK_DEPENDS, **kw): - """ this function prepares the packageroot directory for packaging with the - ipkg builder. - """ - SCons.Tool.Tool('ipkg').generate(env) - - # setup the Ipkg builder - bld = env['BUILDERS']['Ipkg'] - target, source = stripinstallbuilder(target, source, env) - target, source = putintopackageroot(target, source, env, PACKAGEROOT) - - # This should be overridable from the construction environment, - # which it is by using ARCHITECTURE=. - # Guessing based on what os.uname() returns at least allows it - # to work for both i386 and x86_64 Linux systems. - archmap = { - 'i686' : 'i386', - 'i586' : 'i386', - 'i486' : 'i386', - } - - buildarchitecture = os.uname()[4] - buildarchitecture = archmap.get(buildarchitecture, buildarchitecture) - - if 'ARCHITECTURE' in kw: - buildarchitecture = kw['ARCHITECTURE'] - - # setup the kw to contain the mandatory arguments to this fucntion. - # do this before calling any builder or setup function - loc=locals() - del loc['kw'] - kw.update(loc) - del kw['source'], kw['target'], kw['env'] - - # generate the specfile - specfile = gen_ipk_dir(PACKAGEROOT, source, env, kw) - - # override the default target. - if str(target[0])=="%s-%s"%(NAME, VERSION): - target=[ "%s_%s_%s.ipk"%(NAME, VERSION, buildarchitecture) ] - - # now apply the Ipkg builder - return bld(env, target, specfile, **kw) - -def gen_ipk_dir(proot, source, env, kw): - # make sure the packageroot is a Dir object. - if SCons.Util.is_String(proot): proot=env.Dir(proot) - - # create the specfile builder - s_bld=SCons.Builder.Builder( - action = build_specfiles, - ) - - # create the specfile targets - spec_target=[] - control=proot.Dir('CONTROL') - spec_target.append(control.File('control')) - spec_target.append(control.File('conffiles')) - spec_target.append(control.File('postrm')) - spec_target.append(control.File('prerm')) - spec_target.append(control.File('postinst')) - spec_target.append(control.File('preinst')) - - # apply the builder to the specfile targets - s_bld(env, spec_target, source, **kw) - - # the packageroot directory does now contain the specfiles. - return proot - -def build_specfiles(source, target, env): - """ filter the targets for the needed files and use the variables in env - to create the specfile. - """ - # - # At first we care for the CONTROL/control file, which is the main file for ipk. - # - # For this we need to open multiple files in random order, so we store into - # a dict so they can be easily accessed. - # - # - opened_files={} - def open_file(needle, haystack): - try: - return opened_files[needle] - except KeyError: - file=filter(lambda x: x.get_path().rfind(needle)!=-1, haystack)[0] - opened_files[needle]=open(file.abspath, 'w') - return opened_files[needle] - - control_file=open_file('control', target) - - if 'X_IPK_DESCRIPTION' not in env: - env['X_IPK_DESCRIPTION']="%s\n %s"%(env['SUMMARY'], - env['DESCRIPTION'].replace('\n', '\n ')) - - - content = """ -Package: $NAME -Version: $VERSION -Priority: $X_IPK_PRIORITY -Section: $X_IPK_SECTION -Source: $SOURCE_URL -Architecture: $ARCHITECTURE -Maintainer: $X_IPK_MAINTAINER -Depends: $X_IPK_DEPENDS -Description: $X_IPK_DESCRIPTION -""" - - control_file.write(env.subst(content)) - - # - # now handle the various other files, which purpose it is to set post-, - # pre-scripts and mark files as config files. - # - # We do so by filtering the source files for files which are marked with - # the "config" tag and afterwards we do the same for x_ipk_postrm, - # x_ipk_prerm, x_ipk_postinst and x_ipk_preinst tags. - # - # The first one will write the name of the file into the file - # CONTROL/configfiles, the latter add the content of the x_ipk_* variable - # into the same named file. - # - for f in [x for x in source if 'PACKAGING_CONFIG' in dir(x)]: - config=open_file('conffiles') - config.write(f.PACKAGING_INSTALL_LOCATION) - config.write('\n') - - for str in 'POSTRM PRERM POSTINST PREINST'.split(): - name="PACKAGING_X_IPK_%s"%str - for f in [x for x in source if name in dir(x)]: - file=open_file(name) - file.write(env[str]) - - # - # close all opened files - for f in opened_files.values(): - f.close() - - # call a user specified function - if 'CHANGE_SPECFILE' in env: - content += env['CHANGE_SPECFILE'](target) - - return 0 - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/packaging/msi.py b/scons/scons-local-2.2.0/SCons/Tool/packaging/msi.py deleted file mode 100644 index dc593b338..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/packaging/msi.py +++ /dev/null @@ -1,527 +0,0 @@ -"""SCons.Tool.packaging.msi - -The msi packager. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Tool/packaging/msi.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os -import SCons -from SCons.Action import Action -from SCons.Builder import Builder - -from xml.dom.minidom import * -from xml.sax.saxutils import escape - -from SCons.Tool.packaging import stripinstallbuilder - -# -# Utility functions -# -def convert_to_id(s, id_set): - """ Some parts of .wxs need an Id attribute (for example: The File and - Directory directives. The charset is limited to A-Z, a-z, digits, - underscores, periods. Each Id must begin with a letter or with a - underscore. Google for "CNDL0015" for information about this. - - Requirements: - * the string created must only contain chars from the target charset. - * the string created must have a minimal editing distance from the - original string. - * the string created must be unique for the whole .wxs file. - - Observation: - * There are 62 chars in the charset. - - Idea: - * filter out forbidden characters. Check for a collision with the help - of the id_set. Add the number of the number of the collision at the - end of the created string. Furthermore care for a correct start of - the string. - """ - charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxyz0123456789_.' - if s[0] in '0123456789.': - s += '_'+s - id = [c for c in s if c in charset] - - # did we already generate an id for this file? - try: - return id_set[id][s] - except KeyError: - # no we did not so initialize with the id - if id not in id_set: id_set[id] = { s : id } - # there is a collision, generate an id which is unique by appending - # the collision number - else: id_set[id][s] = id + str(len(id_set[id])) - - return id_set[id][s] - -def is_dos_short_file_name(file): - """ examine if the given file is in the 8.3 form. - """ - fname, ext = os.path.splitext(file) - proper_ext = len(ext) == 0 or (2 <= len(ext) <= 4) # the ext contains the dot - proper_fname = file.isupper() and len(fname) <= 8 - - return proper_ext and proper_fname - -def gen_dos_short_file_name(file, filename_set): - """ see http://support.microsoft.com/default.aspx?scid=kb;en-us;Q142982 - - These are no complete 8.3 dos short names. The ~ char is missing and - replaced with one character from the filename. WiX warns about such - filenames, since a collision might occur. Google for "CNDL1014" for - more information. - """ - # guard this to not confuse the generation - if is_dos_short_file_name(file): - return file - - fname, ext = os.path.splitext(file) # ext contains the dot - - # first try if it suffices to convert to upper - file = file.upper() - if is_dos_short_file_name(file): - return file - - # strip forbidden characters. - forbidden = '."/[]:;=, ' - fname = [c for c in fname if c not in forbidden] - - # check if we already generated a filename with the same number: - # thisis1.txt, thisis2.txt etc. - duplicate, num = not None, 1 - while duplicate: - shortname = "%s%s" % (fname[:8-len(str(num))].upper(),\ - str(num)) - if len(ext) >= 2: - shortname = "%s%s" % (shortname, ext[:4].upper()) - - duplicate, num = shortname in filename_set, num+1 - - assert( is_dos_short_file_name(shortname) ), 'shortname is %s, longname is %s' % (shortname, file) - filename_set.append(shortname) - return shortname - -def create_feature_dict(files): - """ X_MSI_FEATURE and doc FileTag's can be used to collect files in a - hierarchy. This function collects the files into this hierarchy. - """ - dict = {} - - def add_to_dict( feature, file ): - if not SCons.Util.is_List( feature ): - feature = [ feature ] - - for f in feature: - if f not in dict: - dict[ f ] = [ file ] - else: - dict[ f ].append( file ) - - for file in files: - if hasattr( file, 'PACKAGING_X_MSI_FEATURE' ): - add_to_dict(file.PACKAGING_X_MSI_FEATURE, file) - elif hasattr( file, 'PACKAGING_DOC' ): - add_to_dict( 'PACKAGING_DOC', file ) - else: - add_to_dict( 'default', file ) - - return dict - -def generate_guids(root): - """ generates globally unique identifiers for parts of the xml which need - them. - - Component tags have a special requirement. Their UUID is only allowed to - change if the list of their contained resources has changed. This allows - for clean removal and proper updates. - - To handle this requirement, the uuid is generated with an md5 hashing the - whole subtree of a xml node. - """ - from hashlib import md5 - - # specify which tags need a guid and in which attribute this should be stored. - needs_id = { 'Product' : 'Id', - 'Package' : 'Id', - 'Component' : 'Guid', - } - - # find all XMl nodes matching the key, retrieve their attribute, hash their - # subtree, convert hash to string and add as a attribute to the xml node. - for (key,value) in needs_id.items(): - node_list = root.getElementsByTagName(key) - attribute = value - for node in node_list: - hash = md5(node.toxml()).hexdigest() - hash_str = '%s-%s-%s-%s-%s' % ( hash[:8], hash[8:12], hash[12:16], hash[16:20], hash[20:] ) - node.attributes[attribute] = hash_str - - - -def string_wxsfile(target, source, env): - return "building WiX file %s"%( target[0].path ) - -def build_wxsfile(target, source, env): - """ compiles a .wxs file from the keywords given in env['msi_spec'] and - by analyzing the tree of source nodes and their tags. - """ - file = open(target[0].abspath, 'w') - - try: - # Create a document with the Wix root tag - doc = Document() - root = doc.createElement( 'Wix' ) - root.attributes['xmlns']='http://schemas.microsoft.com/wix/2003/01/wi' - doc.appendChild( root ) - - filename_set = [] # this is to circumvent duplicates in the shortnames - id_set = {} # this is to circumvent duplicates in the ids - - # Create the content - build_wxsfile_header_section(root, env) - build_wxsfile_file_section(root, source, env['NAME'], env['VERSION'], env['VENDOR'], filename_set, id_set) - generate_guids(root) - build_wxsfile_features_section(root, source, env['NAME'], env['VERSION'], env['SUMMARY'], id_set) - build_wxsfile_default_gui(root) - build_license_file(target[0].get_dir(), env) - - # write the xml to a file - file.write( doc.toprettyxml() ) - - # call a user specified function - if 'CHANGE_SPECFILE' in env: - env['CHANGE_SPECFILE'](target, source) - - except KeyError, e: - raise SCons.Errors.UserError( '"%s" package field for MSI is missing.' % e.args[0] ) - -# -# setup function -# -def create_default_directory_layout(root, NAME, VERSION, VENDOR, filename_set): - """ Create the wix default target directory layout and return the innermost - directory. - - We assume that the XML tree delivered in the root argument already contains - the Product tag. - - Everything is put under the PFiles directory property defined by WiX. - After that a directory with the 'VENDOR' tag is placed and then a - directory with the name of the project and its VERSION. This leads to the - following TARGET Directory Layout: - C:\\\\ - Example: C:\Programme\Company\Product-1.2\ - """ - doc = Document() - d1 = doc.createElement( 'Directory' ) - d1.attributes['Id'] = 'TARGETDIR' - d1.attributes['Name'] = 'SourceDir' - - d2 = doc.createElement( 'Directory' ) - d2.attributes['Id'] = 'ProgramFilesFolder' - d2.attributes['Name'] = 'PFiles' - - d3 = doc.createElement( 'Directory' ) - d3.attributes['Id'] = 'VENDOR_folder' - d3.attributes['Name'] = escape( gen_dos_short_file_name( VENDOR, filename_set ) ) - d3.attributes['LongName'] = escape( VENDOR ) - - d4 = doc.createElement( 'Directory' ) - project_folder = "%s-%s" % ( NAME, VERSION ) - d4.attributes['Id'] = 'MY_DEFAULT_FOLDER' - d4.attributes['Name'] = escape( gen_dos_short_file_name( project_folder, filename_set ) ) - d4.attributes['LongName'] = escape( project_folder ) - - d1.childNodes.append( d2 ) - d2.childNodes.append( d3 ) - d3.childNodes.append( d4 ) - - root.getElementsByTagName('Product')[0].childNodes.append( d1 ) - - return d4 - -# -# mandatory and optional file tags -# -def build_wxsfile_file_section(root, files, NAME, VERSION, VENDOR, filename_set, id_set): - """ builds the Component sections of the wxs file with their included files. - - Files need to be specified in 8.3 format and in the long name format, long - filenames will be converted automatically. - - Features are specficied with the 'X_MSI_FEATURE' or 'DOC' FileTag. - """ - root = create_default_directory_layout( root, NAME, VERSION, VENDOR, filename_set ) - components = create_feature_dict( files ) - factory = Document() - - def get_directory( node, dir ): - """ returns the node under the given node representing the directory. - - Returns the component node if dir is None or empty. - """ - if dir == '' or not dir: - return node - - Directory = node - dir_parts = dir.split(os.path.sep) - - # to make sure that our directory ids are unique, the parent folders are - # consecutively added to upper_dir - upper_dir = '' - - # walk down the xml tree finding parts of the directory - dir_parts = [d for d in dir_parts if d != ''] - for d in dir_parts[:]: - already_created = [c for c in Directory.childNodes - if c.nodeName == 'Directory' - and c.attributes['LongName'].value == escape(d)] - - if already_created != []: - Directory = already_created[0] - dir_parts.remove(d) - upper_dir += d - else: - break - - for d in dir_parts: - nDirectory = factory.createElement( 'Directory' ) - nDirectory.attributes['LongName'] = escape( d ) - nDirectory.attributes['Name'] = escape( gen_dos_short_file_name( d, filename_set ) ) - upper_dir += d - nDirectory.attributes['Id'] = convert_to_id( upper_dir, id_set ) - - Directory.childNodes.append( nDirectory ) - Directory = nDirectory - - return Directory - - for file in files: - drive, path = os.path.splitdrive( file.PACKAGING_INSTALL_LOCATION ) - filename = os.path.basename( path ) - dirname = os.path.dirname( path ) - - h = { - # tagname : default value - 'PACKAGING_X_MSI_VITAL' : 'yes', - 'PACKAGING_X_MSI_FILEID' : convert_to_id(filename, id_set), - 'PACKAGING_X_MSI_LONGNAME' : filename, - 'PACKAGING_X_MSI_SHORTNAME' : gen_dos_short_file_name(filename, filename_set), - 'PACKAGING_X_MSI_SOURCE' : file.get_path(), - } - - # fill in the default tags given above. - for k,v in [ (k, v) for (k,v) in h.items() if not hasattr(file, k) ]: - setattr( file, k, v ) - - File = factory.createElement( 'File' ) - File.attributes['LongName'] = escape( file.PACKAGING_X_MSI_LONGNAME ) - File.attributes['Name'] = escape( file.PACKAGING_X_MSI_SHORTNAME ) - File.attributes['Source'] = escape( file.PACKAGING_X_MSI_SOURCE ) - File.attributes['Id'] = escape( file.PACKAGING_X_MSI_FILEID ) - File.attributes['Vital'] = escape( file.PACKAGING_X_MSI_VITAL ) - - # create the Tag under which this file should appear - Component = factory.createElement('Component') - Component.attributes['DiskId'] = '1' - Component.attributes['Id'] = convert_to_id( filename, id_set ) - - # hang the component node under the root node and the file node - # under the component node. - Directory = get_directory( root, dirname ) - Directory.childNodes.append( Component ) - Component.childNodes.append( File ) - -# -# additional functions -# -def build_wxsfile_features_section(root, files, NAME, VERSION, SUMMARY, id_set): - """ This function creates the tag based on the supplied xml tree. - - This is achieved by finding all s and adding them to a default target. - - It should be called after the tree has been built completly. We assume - that a MY_DEFAULT_FOLDER Property is defined in the wxs file tree. - - Furthermore a top-level with the name and VERSION of the software will be created. - - An PACKAGING_X_MSI_FEATURE can either be a string, where the feature - DESCRIPTION will be the same as its title or a Tuple, where the first - part will be its title and the second its DESCRIPTION. - """ - factory = Document() - Feature = factory.createElement('Feature') - Feature.attributes['Id'] = 'complete' - Feature.attributes['ConfigurableDirectory'] = 'MY_DEFAULT_FOLDER' - Feature.attributes['Level'] = '1' - Feature.attributes['Title'] = escape( '%s %s' % (NAME, VERSION) ) - Feature.attributes['Description'] = escape( SUMMARY ) - Feature.attributes['Display'] = 'expand' - - for (feature, files) in create_feature_dict(files).items(): - SubFeature = factory.createElement('Feature') - SubFeature.attributes['Level'] = '1' - - if SCons.Util.is_Tuple(feature): - SubFeature.attributes['Id'] = convert_to_id( feature[0], id_set ) - SubFeature.attributes['Title'] = escape(feature[0]) - SubFeature.attributes['Description'] = escape(feature[1]) - else: - SubFeature.attributes['Id'] = convert_to_id( feature, id_set ) - if feature=='default': - SubFeature.attributes['Description'] = 'Main Part' - SubFeature.attributes['Title'] = 'Main Part' - elif feature=='PACKAGING_DOC': - SubFeature.attributes['Description'] = 'Documentation' - SubFeature.attributes['Title'] = 'Documentation' - else: - SubFeature.attributes['Description'] = escape(feature) - SubFeature.attributes['Title'] = escape(feature) - - # build the componentrefs. As one of the design decision is that every - # file is also a component we walk the list of files and create a - # reference. - for f in files: - ComponentRef = factory.createElement('ComponentRef') - ComponentRef.attributes['Id'] = convert_to_id( os.path.basename(f.get_path()), id_set ) - SubFeature.childNodes.append(ComponentRef) - - Feature.childNodes.append(SubFeature) - - root.getElementsByTagName('Product')[0].childNodes.append(Feature) - -def build_wxsfile_default_gui(root): - """ this function adds a default GUI to the wxs file - """ - factory = Document() - Product = root.getElementsByTagName('Product')[0] - - UIRef = factory.createElement('UIRef') - UIRef.attributes['Id'] = 'WixUI_Mondo' - Product.childNodes.append(UIRef) - - UIRef = factory.createElement('UIRef') - UIRef.attributes['Id'] = 'WixUI_ErrorProgressText' - Product.childNodes.append(UIRef) - -def build_license_file(directory, spec): - """ creates a License.rtf file with the content of "X_MSI_LICENSE_TEXT" - in the given directory - """ - name, text = '', '' - - try: - name = spec['LICENSE'] - text = spec['X_MSI_LICENSE_TEXT'] - except KeyError: - pass # ignore this as X_MSI_LICENSE_TEXT is optional - - if name!='' or text!='': - file = open( os.path.join(directory.get_path(), 'License.rtf'), 'w' ) - file.write('{\\rtf') - if text!='': - file.write(text.replace('\n', '\\par ')) - else: - file.write(name+'\\par\\par') - file.write('}') - file.close() - -# -# mandatory and optional package tags -# -def build_wxsfile_header_section(root, spec): - """ Adds the xml file node which define the package meta-data. - """ - # Create the needed DOM nodes and add them at the correct position in the tree. - factory = Document() - Product = factory.createElement( 'Product' ) - Package = factory.createElement( 'Package' ) - - root.childNodes.append( Product ) - Product.childNodes.append( Package ) - - # set "mandatory" default values - if 'X_MSI_LANGUAGE' not in spec: - spec['X_MSI_LANGUAGE'] = '1033' # select english - - # mandatory sections, will throw a KeyError if the tag is not available - Product.attributes['Name'] = escape( spec['NAME'] ) - Product.attributes['Version'] = escape( spec['VERSION'] ) - Product.attributes['Manufacturer'] = escape( spec['VENDOR'] ) - Product.attributes['Language'] = escape( spec['X_MSI_LANGUAGE'] ) - Package.attributes['Description'] = escape( spec['SUMMARY'] ) - - # now the optional tags, for which we avoid the KeyErrror exception - if 'DESCRIPTION' in spec: - Package.attributes['Comments'] = escape( spec['DESCRIPTION'] ) - - if 'X_MSI_UPGRADE_CODE' in spec: - Package.attributes['X_MSI_UPGRADE_CODE'] = escape( spec['X_MSI_UPGRADE_CODE'] ) - - # We hardcode the media tag as our current model cannot handle it. - Media = factory.createElement('Media') - Media.attributes['Id'] = '1' - Media.attributes['Cabinet'] = 'default.cab' - Media.attributes['EmbedCab'] = 'yes' - root.getElementsByTagName('Product')[0].childNodes.append(Media) - -# this builder is the entry-point for .wxs file compiler. -wxs_builder = Builder( - action = Action( build_wxsfile, string_wxsfile ), - ensure_suffix = '.wxs' ) - -def package(env, target, source, PACKAGEROOT, NAME, VERSION, - DESCRIPTION, SUMMARY, VENDOR, X_MSI_LANGUAGE, **kw): - # make sure that the Wix Builder is in the environment - SCons.Tool.Tool('wix').generate(env) - - # get put the keywords for the specfile compiler. These are the arguments - # given to the package function and all optional ones stored in kw, minus - # the the source, target and env one. - loc = locals() - del loc['kw'] - kw.update(loc) - del kw['source'], kw['target'], kw['env'] - - # strip the install builder from the source files - target, source = stripinstallbuilder(target, source, env) - - # put the arguments into the env and call the specfile builder. - env['msi_spec'] = kw - specfile = wxs_builder(* [env, target, source], **kw) - - # now call the WiX Tool with the built specfile added as a source. - msifile = env.WiX(target, specfile) - - # return the target and source tuple. - return (msifile, source+[specfile]) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/packaging/rpm.py b/scons/scons-local-2.2.0/SCons/Tool/packaging/rpm.py deleted file mode 100644 index 1c83b6bba..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/packaging/rpm.py +++ /dev/null @@ -1,365 +0,0 @@ -"""SCons.Tool.Packaging.rpm - -The rpm packager. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Tool/packaging/rpm.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os - -import SCons.Builder - -from SCons.Environment import OverrideEnvironment -from SCons.Tool.packaging import stripinstallbuilder, src_targz -from SCons.Errors import UserError - -def package(env, target, source, PACKAGEROOT, NAME, VERSION, - PACKAGEVERSION, DESCRIPTION, SUMMARY, X_RPM_GROUP, LICENSE, - **kw): - # initialize the rpm tool - SCons.Tool.Tool('rpm').generate(env) - - bld = env['BUILDERS']['Rpm'] - - # Generate a UserError whenever the target name has been set explicitly, - # since rpm does not allow for controlling it. This is detected by - # checking if the target has been set to the default by the Package() - # Environment function. - if str(target[0])!="%s-%s"%(NAME, VERSION): - raise UserError( "Setting target is not supported for rpm." ) - else: - # This should be overridable from the construction environment, - # which it is by using ARCHITECTURE=. - # Guessing based on what os.uname() returns at least allows it - # to work for both i386 and x86_64 Linux systems. - archmap = { - 'i686' : 'i386', - 'i586' : 'i386', - 'i486' : 'i386', - } - - buildarchitecture = os.uname()[4] - buildarchitecture = archmap.get(buildarchitecture, buildarchitecture) - - if 'ARCHITECTURE' in kw: - buildarchitecture = kw['ARCHITECTURE'] - - fmt = '%s-%s-%s.%s.rpm' - srcrpm = fmt % (NAME, VERSION, PACKAGEVERSION, 'src') - binrpm = fmt % (NAME, VERSION, PACKAGEVERSION, buildarchitecture) - - target = [ srcrpm, binrpm ] - - # get the correct arguments into the kw hash - loc=locals() - del loc['kw'] - kw.update(loc) - del kw['source'], kw['target'], kw['env'] - - # if no "SOURCE_URL" tag is given add a default one. - if 'SOURCE_URL' not in kw: - #kw['SOURCE_URL']=(str(target[0])+".tar.gz").replace('.rpm', '') - kw['SOURCE_URL']=(str(target[0])+".tar.gz").replace('.rpm', '') - - # mangle the source and target list for the rpmbuild - env = OverrideEnvironment(env, kw) - target, source = stripinstallbuilder(target, source, env) - target, source = addspecfile(target, source, env) - target, source = collectintargz(target, source, env) - - # now call the rpm builder to actually build the packet. - return bld(env, target, source, **kw) - -def collectintargz(target, source, env): - """ Puts all source files into a tar.gz file. """ - # the rpm tool depends on a source package, until this is chagned - # this hack needs to be here that tries to pack all sources in. - sources = env.FindSourceFiles() - - # filter out the target we are building the source list for. - #sources = [s for s in sources if not (s in target)] - sources = [s for s in sources if s not in target] - - # find the .spec file for rpm and add it since it is not necessarily found - # by the FindSourceFiles function. - #sources.extend( [s for s in source if str(s).rfind('.spec')!=-1] ) - spec_file = lambda s: str(s).rfind('.spec') != -1 - sources.extend( list(filter(spec_file, source)) ) - - # as the source contains the url of the source package this rpm package - # is built from, we extract the target name - #tarball = (str(target[0])+".tar.gz").replace('.rpm', '') - tarball = (str(target[0])+".tar.gz").replace('.rpm', '') - try: - #tarball = env['SOURCE_URL'].split('/')[-1] - tarball = env['SOURCE_URL'].split('/')[-1] - except KeyError, e: - raise SCons.Errors.UserError( "Missing PackageTag '%s' for RPM packager" % e.args[0] ) - - tarball = src_targz.package(env, source=sources, target=tarball, - PACKAGEROOT=env['PACKAGEROOT'], ) - - return (target, tarball) - -def addspecfile(target, source, env): - specfile = "%s-%s" % (env['NAME'], env['VERSION']) - - bld = SCons.Builder.Builder(action = build_specfile, - suffix = '.spec', - target_factory = SCons.Node.FS.File) - - source.extend(bld(env, specfile, source)) - - return (target,source) - -def build_specfile(target, source, env): - """ Builds a RPM specfile from a dictionary with string metadata and - by analyzing a tree of nodes. - """ - file = open(target[0].abspath, 'w') - str = "" - - try: - file.write( build_specfile_header(env) ) - file.write( build_specfile_sections(env) ) - file.write( build_specfile_filesection(env, source) ) - file.close() - - # call a user specified function - if 'CHANGE_SPECFILE' in env: - env['CHANGE_SPECFILE'](target, source) - - except KeyError, e: - raise SCons.Errors.UserError( '"%s" package field for RPM is missing.' % e.args[0] ) - - -# -# mandatory and optional package tag section -# -def build_specfile_sections(spec): - """ Builds the sections of a rpm specfile. - """ - str = "" - - mandatory_sections = { - 'DESCRIPTION' : '\n%%description\n%s\n\n', } - - str = str + SimpleTagCompiler(mandatory_sections).compile( spec ) - - optional_sections = { - 'DESCRIPTION_' : '%%description -l %s\n%s\n\n', - 'CHANGELOG' : '%%changelog\n%s\n\n', - 'X_RPM_PREINSTALL' : '%%pre\n%s\n\n', - 'X_RPM_POSTINSTALL' : '%%post\n%s\n\n', - 'X_RPM_PREUNINSTALL' : '%%preun\n%s\n\n', - 'X_RPM_POSTUNINSTALL' : '%%postun\n%s\n\n', - 'X_RPM_VERIFY' : '%%verify\n%s\n\n', - - # These are for internal use but could possibly be overriden - 'X_RPM_PREP' : '%%prep\n%s\n\n', - 'X_RPM_BUILD' : '%%build\n%s\n\n', - 'X_RPM_INSTALL' : '%%install\n%s\n\n', - 'X_RPM_CLEAN' : '%%clean\n%s\n\n', - } - - # Default prep, build, install and clean rules - # TODO: optimize those build steps, to not compile the project a second time - if 'X_RPM_PREP' not in spec: - spec['X_RPM_PREP'] = '[ -n "$RPM_BUILD_ROOT" -a "$RPM_BUILD_ROOT" != / ] && rm -rf "$RPM_BUILD_ROOT"' + '\n%setup -q' - - if 'X_RPM_BUILD' not in spec: - spec['X_RPM_BUILD'] = 'mkdir "$RPM_BUILD_ROOT"' - - if 'X_RPM_INSTALL' not in spec: - spec['X_RPM_INSTALL'] = 'scons --install-sandbox="$RPM_BUILD_ROOT" "$RPM_BUILD_ROOT"' - - if 'X_RPM_CLEAN' not in spec: - spec['X_RPM_CLEAN'] = '[ -n "$RPM_BUILD_ROOT" -a "$RPM_BUILD_ROOT" != / ] && rm -rf "$RPM_BUILD_ROOT"' - - str = str + SimpleTagCompiler(optional_sections, mandatory=0).compile( spec ) - - return str - -def build_specfile_header(spec): - """ Builds all section but the %file of a rpm specfile - """ - str = "" - - # first the mandatory sections - mandatory_header_fields = { - 'NAME' : '%%define name %s\nName: %%{name}\n', - 'VERSION' : '%%define version %s\nVersion: %%{version}\n', - 'PACKAGEVERSION' : '%%define release %s\nRelease: %%{release}\n', - 'X_RPM_GROUP' : 'Group: %s\n', - 'SUMMARY' : 'Summary: %s\n', - 'LICENSE' : 'License: %s\n', } - - str = str + SimpleTagCompiler(mandatory_header_fields).compile( spec ) - - # now the optional tags - optional_header_fields = { - 'VENDOR' : 'Vendor: %s\n', - 'X_RPM_URL' : 'Url: %s\n', - 'SOURCE_URL' : 'Source: %s\n', - 'SUMMARY_' : 'Summary(%s): %s\n', - 'X_RPM_DISTRIBUTION' : 'Distribution: %s\n', - 'X_RPM_ICON' : 'Icon: %s\n', - 'X_RPM_PACKAGER' : 'Packager: %s\n', - 'X_RPM_GROUP_' : 'Group(%s): %s\n', - - 'X_RPM_REQUIRES' : 'Requires: %s\n', - 'X_RPM_PROVIDES' : 'Provides: %s\n', - 'X_RPM_CONFLICTS' : 'Conflicts: %s\n', - 'X_RPM_BUILDREQUIRES' : 'BuildRequires: %s\n', - - 'X_RPM_SERIAL' : 'Serial: %s\n', - 'X_RPM_EPOCH' : 'Epoch: %s\n', - 'X_RPM_AUTOREQPROV' : 'AutoReqProv: %s\n', - 'X_RPM_EXCLUDEARCH' : 'ExcludeArch: %s\n', - 'X_RPM_EXCLUSIVEARCH' : 'ExclusiveArch: %s\n', - 'X_RPM_PREFIX' : 'Prefix: %s\n', - 'X_RPM_CONFLICTS' : 'Conflicts: %s\n', - - # internal use - 'X_RPM_BUILDROOT' : 'BuildRoot: %s\n', } - - # fill in default values: - # Adding a BuildRequires renders the .rpm unbuildable under System, which - # are not managed by rpm, since the database to resolve this dependency is - # missing (take Gentoo as an example) -# if not s.has_key('x_rpm_BuildRequires'): -# s['x_rpm_BuildRequires'] = 'scons' - - if 'X_RPM_BUILDROOT' not in spec: - spec['X_RPM_BUILDROOT'] = '%{_tmppath}/%{name}-%{version}-%{release}' - - str = str + SimpleTagCompiler(optional_header_fields, mandatory=0).compile( spec ) - return str - -# -# mandatory and optional file tags -# -def build_specfile_filesection(spec, files): - """ builds the %file section of the specfile - """ - str = '%files\n' - - if 'X_RPM_DEFATTR' not in spec: - spec['X_RPM_DEFATTR'] = '(-,root,root)' - - str = str + '%%defattr %s\n' % spec['X_RPM_DEFATTR'] - - supported_tags = { - 'PACKAGING_CONFIG' : '%%config %s', - 'PACKAGING_CONFIG_NOREPLACE' : '%%config(noreplace) %s', - 'PACKAGING_DOC' : '%%doc %s', - 'PACKAGING_UNIX_ATTR' : '%%attr %s', - 'PACKAGING_LANG_' : '%%lang(%s) %s', - 'PACKAGING_X_RPM_VERIFY' : '%%verify %s', - 'PACKAGING_X_RPM_DIR' : '%%dir %s', - 'PACKAGING_X_RPM_DOCDIR' : '%%docdir %s', - 'PACKAGING_X_RPM_GHOST' : '%%ghost %s', } - - for file in files: - # build the tagset - tags = {} - for k in supported_tags.keys(): - try: - tags[k]=getattr(file, k) - except AttributeError: - pass - - # compile the tagset - str = str + SimpleTagCompiler(supported_tags, mandatory=0).compile( tags ) - - str = str + ' ' - str = str + file.PACKAGING_INSTALL_LOCATION - str = str + '\n\n' - - return str - -class SimpleTagCompiler(object): - """ This class is a simple string substition utility: - the replacement specfication is stored in the tagset dictionary, something - like: - { "abc" : "cdef %s ", - "abc_" : "cdef %s %s" } - - the compile function gets a value dictionary, which may look like: - { "abc" : "ghij", - "abc_gh" : "ij" } - - The resulting string will be: - "cdef ghij cdef gh ij" - """ - def __init__(self, tagset, mandatory=1): - self.tagset = tagset - self.mandatory = mandatory - - def compile(self, values): - """ compiles the tagset and returns a str containing the result - """ - def is_international(tag): - #return tag.endswith('_') - return tag[-1:] == '_' - - def get_country_code(tag): - return tag[-2:] - - def strip_country_code(tag): - return tag[:-2] - - replacements = list(self.tagset.items()) - - str = "" - #domestic = [ (k,v) for k,v in replacements if not is_international(k) ] - domestic = [t for t in replacements if not is_international(t[0])] - for key, replacement in domestic: - try: - str = str + replacement % values[key] - except KeyError, e: - if self.mandatory: - raise e - - #international = [ (k,v) for k,v in replacements if is_international(k) ] - international = [t for t in replacements if is_international(t[0])] - for key, replacement in international: - try: - #int_values_for_key = [ (get_country_code(k),v) for k,v in values.items() if strip_country_code(k) == key ] - x = [t for t in values.items() if strip_country_code(t[0]) == key] - int_values_for_key = [(get_country_code(t[0]),t[1]) for t in x] - for v in int_values_for_key: - str = str + replacement % v - except KeyError, e: - if self.mandatory: - raise e - - return str - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/packaging/src_tarbz2.py b/scons/scons-local-2.2.0/SCons/Tool/packaging/src_tarbz2.py deleted file mode 100644 index 41c37ac4b..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/packaging/src_tarbz2.py +++ /dev/null @@ -1,43 +0,0 @@ -"""SCons.Tool.Packaging.tarbz2 - -The tarbz2 SRC packager. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/packaging/src_tarbz2.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -from SCons.Tool.packaging import putintopackageroot - -def package(env, target, source, PACKAGEROOT, **kw): - bld = env['BUILDERS']['Tar'] - bld.set_suffix('.tar.bz2') - target, source = putintopackageroot(target, source, env, PACKAGEROOT, honor_install_location=0) - return bld(env, target, source, TARFLAGS='-jc') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/packaging/src_targz.py b/scons/scons-local-2.2.0/SCons/Tool/packaging/src_targz.py deleted file mode 100644 index dbf1c2bbe..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/packaging/src_targz.py +++ /dev/null @@ -1,43 +0,0 @@ -"""SCons.Tool.Packaging.targz - -The targz SRC packager. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/packaging/src_targz.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -from SCons.Tool.packaging import putintopackageroot - -def package(env, target, source, PACKAGEROOT, **kw): - bld = env['BUILDERS']['Tar'] - bld.set_suffix('.tar.gz') - target, source = putintopackageroot(target, source, env, PACKAGEROOT, honor_install_location=0) - return bld(env, target, source, TARFLAGS='-zc') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/packaging/src_zip.py b/scons/scons-local-2.2.0/SCons/Tool/packaging/src_zip.py deleted file mode 100644 index e12c3667b..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/packaging/src_zip.py +++ /dev/null @@ -1,43 +0,0 @@ -"""SCons.Tool.Packaging.zip - -The zip SRC packager. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/packaging/src_zip.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -from SCons.Tool.packaging import putintopackageroot - -def package(env, target, source, PACKAGEROOT, **kw): - bld = env['BUILDERS']['Zip'] - bld.set_suffix('.zip') - target, source = putintopackageroot(target, source, env, PACKAGEROOT, honor_install_location=0) - return bld(env, target, source) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/packaging/tarbz2.py b/scons/scons-local-2.2.0/SCons/Tool/packaging/tarbz2.py deleted file mode 100644 index 73964eb83..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/packaging/tarbz2.py +++ /dev/null @@ -1,44 +0,0 @@ -"""SCons.Tool.Packaging.tarbz2 - -The tarbz2 SRC packager. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/packaging/tarbz2.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot - -def package(env, target, source, PACKAGEROOT, **kw): - bld = env['BUILDERS']['Tar'] - bld.set_suffix('.tar.gz') - target, source = putintopackageroot(target, source, env, PACKAGEROOT) - target, source = stripinstallbuilder(target, source, env) - return bld(env, target, source, TARFLAGS='-jc') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/packaging/targz.py b/scons/scons-local-2.2.0/SCons/Tool/packaging/targz.py deleted file mode 100644 index 1019bdbf5..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/packaging/targz.py +++ /dev/null @@ -1,44 +0,0 @@ -"""SCons.Tool.Packaging.targz - -The targz SRC packager. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/packaging/targz.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot - -def package(env, target, source, PACKAGEROOT, **kw): - bld = env['BUILDERS']['Tar'] - bld.set_suffix('.tar.gz') - target, source = stripinstallbuilder(target, source, env) - target, source = putintopackageroot(target, source, env, PACKAGEROOT) - return bld(env, target, source, TARFLAGS='-zc') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/packaging/zip.py b/scons/scons-local-2.2.0/SCons/Tool/packaging/zip.py deleted file mode 100644 index a96278749..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/packaging/zip.py +++ /dev/null @@ -1,44 +0,0 @@ -"""SCons.Tool.Packaging.zip - -The zip SRC packager. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/packaging/zip.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot - -def package(env, target, source, PACKAGEROOT, **kw): - bld = env['BUILDERS']['Zip'] - bld.set_suffix('.zip') - target, source = stripinstallbuilder(target, source, env) - target, source = putintopackageroot(target, source, env, PACKAGEROOT) - return bld(env, target, source) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/pdf.py b/scons/scons-local-2.2.0/SCons/Tool/pdf.py deleted file mode 100644 index beae6dd2a..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/pdf.py +++ /dev/null @@ -1,78 +0,0 @@ -"""SCons.Tool.pdf - -Common PDF Builder definition for various other Tool modules that use it. -Add an explicit action to run epstopdf to convert .eps files to .pdf - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/pdf.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Builder -import SCons.Tool - -PDFBuilder = None - -EpsPdfAction = SCons.Action.Action('$EPSTOPDFCOM', '$EPSTOPDFCOMSTR') - -def generate(env): - try: - env['BUILDERS']['PDF'] - except KeyError: - global PDFBuilder - if PDFBuilder is None: - PDFBuilder = SCons.Builder.Builder(action = {}, - source_scanner = SCons.Tool.PDFLaTeXScanner, - prefix = '$PDFPREFIX', - suffix = '$PDFSUFFIX', - emitter = {}, - source_ext_match = None, - single_source=True) - env['BUILDERS']['PDF'] = PDFBuilder - - env['PDFPREFIX'] = '' - env['PDFSUFFIX'] = '.pdf' - -# put the epstopdf builder in this routine so we can add it after -# the pdftex builder so that one is the default for no source suffix -def generate2(env): - bld = env['BUILDERS']['PDF'] - #bld.add_action('.ps', EpsPdfAction) # this is covered by direct Ghostcript action in gs.py - bld.add_action('.eps', EpsPdfAction) - - env['EPSTOPDF'] = 'epstopdf' - env['EPSTOPDFFLAGS'] = SCons.Util.CLVar('') - env['EPSTOPDFCOM'] = '$EPSTOPDF $EPSTOPDFFLAGS ${SOURCE} --outfile=${TARGET}' - -def exists(env): - # This only puts a skeleton Builder in place, so if someone - # references this Tool directly, it's always "available." - return 1 - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/pdflatex.py b/scons/scons-local-2.2.0/SCons/Tool/pdflatex.py deleted file mode 100644 index 9d2a449ec..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/pdflatex.py +++ /dev/null @@ -1,84 +0,0 @@ -"""SCons.Tool.pdflatex - -Tool-specific initialization for pdflatex. -Generates .pdf files from .latex or .ltx files - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/pdflatex.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Action -import SCons.Util -import SCons.Tool.pdf -import SCons.Tool.tex - -PDFLaTeXAction = None - -def PDFLaTeXAuxFunction(target = None, source= None, env=None): - result = SCons.Tool.tex.InternalLaTeXAuxAction( PDFLaTeXAction, target, source, env ) - if result != 0: - SCons.Tool.tex.check_file_error_message(env['PDFLATEX']) - return result - -PDFLaTeXAuxAction = None - -def generate(env): - """Add Builders and construction variables for pdflatex to an Environment.""" - global PDFLaTeXAction - if PDFLaTeXAction is None: - PDFLaTeXAction = SCons.Action.Action('$PDFLATEXCOM', '$PDFLATEXCOMSTR') - - global PDFLaTeXAuxAction - if PDFLaTeXAuxAction is None: - PDFLaTeXAuxAction = SCons.Action.Action(PDFLaTeXAuxFunction, - strfunction=SCons.Tool.tex.TeXLaTeXStrFunction) - - env.AppendUnique(LATEXSUFFIXES=SCons.Tool.LaTeXSuffixes) - - import pdf - pdf.generate(env) - - bld = env['BUILDERS']['PDF'] - bld.add_action('.ltx', PDFLaTeXAuxAction) - bld.add_action('.latex', PDFLaTeXAuxAction) - bld.add_emitter('.ltx', SCons.Tool.tex.tex_pdf_emitter) - bld.add_emitter('.latex', SCons.Tool.tex.tex_pdf_emitter) - - SCons.Tool.tex.generate_common(env) - -def exists(env): - SCons.Tool.tex.generate_darwin(env) - return env.Detect('pdflatex') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/pdftex.py b/scons/scons-local-2.2.0/SCons/Tool/pdftex.py deleted file mode 100644 index b5898c17f..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/pdftex.py +++ /dev/null @@ -1,109 +0,0 @@ -"""SCons.Tool.pdftex - -Tool-specific initialization for pdftex. -Generates .pdf files from .tex files - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/pdftex.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os -import SCons.Action -import SCons.Util -import SCons.Tool.tex - -PDFTeXAction = None - -# This action might be needed more than once if we are dealing with -# labels and bibtex. -PDFLaTeXAction = None - -def PDFLaTeXAuxAction(target = None, source= None, env=None): - result = SCons.Tool.tex.InternalLaTeXAuxAction( PDFLaTeXAction, target, source, env ) - return result - -def PDFTeXLaTeXFunction(target = None, source= None, env=None): - """A builder for TeX and LaTeX that scans the source file to - decide the "flavor" of the source and then executes the appropriate - program.""" - basedir = os.path.split(str(source[0]))[0] - abspath = os.path.abspath(basedir) - - if SCons.Tool.tex.is_LaTeX(source,env,abspath): - result = PDFLaTeXAuxAction(target,source,env) - if result != 0: - SCons.Tool.tex.check_file_error_message(env['PDFLATEX']) - else: - result = PDFTeXAction(target,source,env) - if result != 0: - SCons.Tool.tex.check_file_error_message(env['PDFTEX']) - return result - -PDFTeXLaTeXAction = None - -def generate(env): - """Add Builders and construction variables for pdftex to an Environment.""" - global PDFTeXAction - if PDFTeXAction is None: - PDFTeXAction = SCons.Action.Action('$PDFTEXCOM', '$PDFTEXCOMSTR') - - global PDFLaTeXAction - if PDFLaTeXAction is None: - PDFLaTeXAction = SCons.Action.Action("$PDFLATEXCOM", "$PDFLATEXCOMSTR") - - global PDFTeXLaTeXAction - if PDFTeXLaTeXAction is None: - PDFTeXLaTeXAction = SCons.Action.Action(PDFTeXLaTeXFunction, - strfunction=SCons.Tool.tex.TeXLaTeXStrFunction) - - env.AppendUnique(LATEXSUFFIXES=SCons.Tool.LaTeXSuffixes) - - import pdf - pdf.generate(env) - - bld = env['BUILDERS']['PDF'] - bld.add_action('.tex', PDFTeXLaTeXAction) - bld.add_emitter('.tex', SCons.Tool.tex.tex_pdf_emitter) - - # Add the epstopdf builder after the pdftex builder - # so pdftex is the default for no source suffix - pdf.generate2(env) - - SCons.Tool.tex.generate_common(env) - -def exists(env): - SCons.Tool.tex.generate_darwin(env) - return env.Detect('pdftex') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/qt.py b/scons/scons-local-2.2.0/SCons/Tool/qt.py deleted file mode 100644 index d40337d44..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/qt.py +++ /dev/null @@ -1,336 +0,0 @@ - -"""SCons.Tool.qt - -Tool-specific initialization for Qt. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/qt.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os.path -import re - -import SCons.Action -import SCons.Builder -import SCons.Defaults -import SCons.Scanner -import SCons.Tool -import SCons.Util - -class ToolQtWarning(SCons.Warnings.Warning): - pass - -class GeneratedMocFileNotIncluded(ToolQtWarning): - pass - -class QtdirNotFound(ToolQtWarning): - pass - -SCons.Warnings.enableWarningClass(ToolQtWarning) - -header_extensions = [".h", ".hxx", ".hpp", ".hh"] -if SCons.Util.case_sensitive_suffixes('.h', '.H'): - header_extensions.append('.H') -cplusplus = __import__('c++', globals(), locals(), []) -cxx_suffixes = cplusplus.CXXSuffixes - -def checkMocIncluded(target, source, env): - moc = target[0] - cpp = source[0] - # looks like cpp.includes is cleared before the build stage :-( - # not really sure about the path transformations (moc.cwd? cpp.cwd?) :-/ - path = SCons.Defaults.CScan.path(env, moc.cwd) - includes = SCons.Defaults.CScan(cpp, env, path) - if not moc in includes: - SCons.Warnings.warn( - GeneratedMocFileNotIncluded, - "Generated moc file '%s' is not included by '%s'" % - (str(moc), str(cpp))) - -def find_file(filename, paths, node_factory): - for dir in paths: - node = node_factory(filename, dir) - if node.rexists(): - return node - return None - -class _Automoc(object): - """ - Callable class, which works as an emitter for Programs, SharedLibraries and - StaticLibraries. - """ - - def __init__(self, objBuilderName): - self.objBuilderName = objBuilderName - - def __call__(self, target, source, env): - """ - Smart autoscan function. Gets the list of objects for the Program - or Lib. Adds objects and builders for the special qt files. - """ - try: - if int(env.subst('$QT_AUTOSCAN')) == 0: - return target, source - except ValueError: - pass - try: - debug = int(env.subst('$QT_DEBUG')) - except ValueError: - debug = 0 - - # some shortcuts used in the scanner - splitext = SCons.Util.splitext - objBuilder = getattr(env, self.objBuilderName) - - # some regular expressions: - # Q_OBJECT detection - q_object_search = re.compile(r'[^A-Za-z0-9]Q_OBJECT[^A-Za-z0-9]') - # cxx and c comment 'eater' - #comment = re.compile(r'(//.*)|(/\*(([^*])|(\*[^/]))*\*/)') - # CW: something must be wrong with the regexp. See also bug #998222 - # CURRENTLY THERE IS NO TEST CASE FOR THAT - - # The following is kind of hacky to get builders working properly (FIXME) - objBuilderEnv = objBuilder.env - objBuilder.env = env - mocBuilderEnv = env.Moc.env - env.Moc.env = env - - # make a deep copy for the result; MocH objects will be appended - out_sources = source[:] - - for obj in source: - if not obj.has_builder(): - # binary obj file provided - if debug: - print "scons: qt: '%s' seems to be a binary. Discarded." % str(obj) - continue - cpp = obj.sources[0] - if not splitext(str(cpp))[1] in cxx_suffixes: - if debug: - print "scons: qt: '%s' is no cxx file. Discarded." % str(cpp) - # c or fortran source - continue - #cpp_contents = comment.sub('', cpp.get_text_contents()) - cpp_contents = cpp.get_text_contents() - h=None - for h_ext in header_extensions: - # try to find the header file in the corresponding source - # directory - hname = splitext(cpp.name)[0] + h_ext - h = find_file(hname, (cpp.get_dir(),), env.File) - if h: - if debug: - print "scons: qt: Scanning '%s' (header of '%s')" % (str(h), str(cpp)) - #h_contents = comment.sub('', h.get_text_contents()) - h_contents = h.get_text_contents() - break - if not h and debug: - print "scons: qt: no header for '%s'." % (str(cpp)) - if h and q_object_search.search(h_contents): - # h file with the Q_OBJECT macro found -> add moc_cpp - moc_cpp = env.Moc(h) - moc_o = objBuilder(moc_cpp) - out_sources.append(moc_o) - #moc_cpp.target_scanner = SCons.Defaults.CScan - if debug: - print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(h), str(moc_cpp)) - if cpp and q_object_search.search(cpp_contents): - # cpp file with Q_OBJECT macro found -> add moc - # (to be included in cpp) - moc = env.Moc(cpp) - env.Ignore(moc, moc) - if debug: - print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(cpp), str(moc)) - #moc.source_scanner = SCons.Defaults.CScan - # restore the original env attributes (FIXME) - objBuilder.env = objBuilderEnv - env.Moc.env = mocBuilderEnv - - return (target, out_sources) - -AutomocShared = _Automoc('SharedObject') -AutomocStatic = _Automoc('StaticObject') - -def _detect(env): - """Not really safe, but fast method to detect the QT library""" - QTDIR = None - if not QTDIR: - QTDIR = env.get('QTDIR',None) - if not QTDIR: - QTDIR = os.environ.get('QTDIR',None) - if not QTDIR: - moc = env.WhereIs('moc') - if moc: - QTDIR = os.path.dirname(os.path.dirname(moc)) - SCons.Warnings.warn( - QtdirNotFound, - "Could not detect qt, using moc executable as a hint (QTDIR=%s)" % QTDIR) - else: - QTDIR = None - SCons.Warnings.warn( - QtdirNotFound, - "Could not detect qt, using empty QTDIR") - return QTDIR - -def uicEmitter(target, source, env): - adjustixes = SCons.Util.adjustixes - bs = SCons.Util.splitext(str(source[0].name))[0] - bs = os.path.join(str(target[0].get_dir()),bs) - # first target (header) is automatically added by builder - if len(target) < 2: - # second target is implementation - target.append(adjustixes(bs, - env.subst('$QT_UICIMPLPREFIX'), - env.subst('$QT_UICIMPLSUFFIX'))) - if len(target) < 3: - # third target is moc file - target.append(adjustixes(bs, - env.subst('$QT_MOCHPREFIX'), - env.subst('$QT_MOCHSUFFIX'))) - return target, source - -def uicScannerFunc(node, env, path): - lookout = [] - lookout.extend(env['CPPPATH']) - lookout.append(str(node.rfile().dir)) - includes = re.findall("(.*?)", node.get_text_contents()) - result = [] - for incFile in includes: - dep = env.FindFile(incFile,lookout) - if dep: - result.append(dep) - return result - -uicScanner = SCons.Scanner.Base(uicScannerFunc, - name = "UicScanner", - node_class = SCons.Node.FS.File, - node_factory = SCons.Node.FS.File, - recursive = 0) - -def generate(env): - """Add Builders and construction variables for qt to an Environment.""" - CLVar = SCons.Util.CLVar - Action = SCons.Action.Action - Builder = SCons.Builder.Builder - - env.SetDefault(QTDIR = _detect(env), - QT_BINPATH = os.path.join('$QTDIR', 'bin'), - QT_CPPPATH = os.path.join('$QTDIR', 'include'), - QT_LIBPATH = os.path.join('$QTDIR', 'lib'), - QT_MOC = os.path.join('$QT_BINPATH','moc'), - QT_UIC = os.path.join('$QT_BINPATH','uic'), - QT_LIB = 'qt', # may be set to qt-mt - - QT_AUTOSCAN = 1, # scan for moc'able sources - - # Some QT specific flags. I don't expect someone wants to - # manipulate those ... - QT_UICIMPLFLAGS = CLVar(''), - QT_UICDECLFLAGS = CLVar(''), - QT_MOCFROMHFLAGS = CLVar(''), - QT_MOCFROMCXXFLAGS = CLVar('-i'), - - # suffixes/prefixes for the headers / sources to generate - QT_UICDECLPREFIX = '', - QT_UICDECLSUFFIX = '.h', - QT_UICIMPLPREFIX = 'uic_', - QT_UICIMPLSUFFIX = '$CXXFILESUFFIX', - QT_MOCHPREFIX = 'moc_', - QT_MOCHSUFFIX = '$CXXFILESUFFIX', - QT_MOCCXXPREFIX = '', - QT_MOCCXXSUFFIX = '.moc', - QT_UISUFFIX = '.ui', - - # Commands for the qt support ... - # command to generate header, implementation and moc-file - # from a .ui file - QT_UICCOM = [ - CLVar('$QT_UIC $QT_UICDECLFLAGS -o ${TARGETS[0]} $SOURCE'), - CLVar('$QT_UIC $QT_UICIMPLFLAGS -impl ${TARGETS[0].file} ' - '-o ${TARGETS[1]} $SOURCE'), - CLVar('$QT_MOC $QT_MOCFROMHFLAGS -o ${TARGETS[2]} ${TARGETS[0]}')], - # command to generate meta object information for a class - # declarated in a header - QT_MOCFROMHCOM = ( - '$QT_MOC $QT_MOCFROMHFLAGS -o ${TARGETS[0]} $SOURCE'), - # command to generate meta object information for a class - # declarated in a cpp file - QT_MOCFROMCXXCOM = [ - CLVar('$QT_MOC $QT_MOCFROMCXXFLAGS -o ${TARGETS[0]} $SOURCE'), - Action(checkMocIncluded,None)]) - - # ... and the corresponding builders - uicBld = Builder(action=SCons.Action.Action('$QT_UICCOM', '$QT_UICCOMSTR'), - emitter=uicEmitter, - src_suffix='$QT_UISUFFIX', - suffix='$QT_UICDECLSUFFIX', - prefix='$QT_UICDECLPREFIX', - source_scanner=uicScanner) - mocBld = Builder(action={}, prefix={}, suffix={}) - for h in header_extensions: - act = SCons.Action.Action('$QT_MOCFROMHCOM', '$QT_MOCFROMHCOMSTR') - mocBld.add_action(h, act) - mocBld.prefix[h] = '$QT_MOCHPREFIX' - mocBld.suffix[h] = '$QT_MOCHSUFFIX' - for cxx in cxx_suffixes: - act = SCons.Action.Action('$QT_MOCFROMCXXCOM', '$QT_MOCFROMCXXCOMSTR') - mocBld.add_action(cxx, act) - mocBld.prefix[cxx] = '$QT_MOCCXXPREFIX' - mocBld.suffix[cxx] = '$QT_MOCCXXSUFFIX' - - # register the builders - env['BUILDERS']['Uic'] = uicBld - env['BUILDERS']['Moc'] = mocBld - static_obj, shared_obj = SCons.Tool.createObjBuilders(env) - static_obj.add_src_builder('Uic') - shared_obj.add_src_builder('Uic') - - # We use the emitters of Program / StaticLibrary / SharedLibrary - # to scan for moc'able files - # We can't refer to the builders directly, we have to fetch them - # as Environment attributes because that sets them up to be called - # correctly later by our emitter. - env.AppendUnique(PROGEMITTER =[AutomocStatic], - SHLIBEMITTER=[AutomocShared], - LIBEMITTER =[AutomocStatic], - # Of course, we need to link against the qt libraries - CPPPATH=["$QT_CPPPATH"], - LIBPATH=["$QT_LIBPATH"], - LIBS=['$QT_LIB']) - -def exists(env): - return _detect(env) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/rmic.py b/scons/scons-local-2.2.0/SCons/Tool/rmic.py deleted file mode 100644 index 0b32f06bd..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/rmic.py +++ /dev/null @@ -1,126 +0,0 @@ -"""SCons.Tool.rmic - -Tool-specific initialization for rmic. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/rmic.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os.path - -import SCons.Action -import SCons.Builder -import SCons.Node.FS -import SCons.Util - -def emit_rmic_classes(target, source, env): - """Create and return lists of Java RMI stub and skeleton - class files to be created from a set of class files. - """ - class_suffix = env.get('JAVACLASSSUFFIX', '.class') - classdir = env.get('JAVACLASSDIR') - - if not classdir: - try: - s = source[0] - except IndexError: - classdir = '.' - else: - try: - classdir = s.attributes.java_classdir - except AttributeError: - classdir = '.' - classdir = env.Dir(classdir).rdir() - if str(classdir) == '.': - c_ = None - else: - c_ = str(classdir) + os.sep - - slist = [] - for src in source: - try: - classname = src.attributes.java_classname - except AttributeError: - classname = str(src) - if c_ and classname[:len(c_)] == c_: - classname = classname[len(c_):] - if class_suffix and classname[:-len(class_suffix)] == class_suffix: - classname = classname[-len(class_suffix):] - s = src.rfile() - s.attributes.java_classdir = classdir - s.attributes.java_classname = classname - slist.append(s) - - stub_suffixes = ['_Stub'] - if env.get('JAVAVERSION') == '1.4': - stub_suffixes.append('_Skel') - - tlist = [] - for s in source: - for suff in stub_suffixes: - fname = s.attributes.java_classname.replace('.', os.sep) + \ - suff + class_suffix - t = target[0].File(fname) - t.attributes.java_lookupdir = target[0] - tlist.append(t) - - return tlist, source - -RMICAction = SCons.Action.Action('$RMICCOM', '$RMICCOMSTR') - -RMICBuilder = SCons.Builder.Builder(action = RMICAction, - emitter = emit_rmic_classes, - src_suffix = '$JAVACLASSSUFFIX', - target_factory = SCons.Node.FS.Dir, - source_factory = SCons.Node.FS.File) - -def generate(env): - """Add Builders and construction variables for rmic to an Environment.""" - env['BUILDERS']['RMIC'] = RMICBuilder - - env['RMIC'] = 'rmic' - env['RMICFLAGS'] = SCons.Util.CLVar('') - env['RMICCOM'] = '$RMIC $RMICFLAGS -d ${TARGET.attributes.java_lookupdir} -classpath ${SOURCE.attributes.java_classdir} ${SOURCES.attributes.java_classname}' - env['JAVACLASSSUFFIX'] = '.class' - -def exists(env): - # As reported by Jan Nijtmans in issue #2730, the simple - # return env.Detect('rmic') - # doesn't always work during initialization. For now, we - # stop trying to detect an executable (analogous to the - # javac Builder). - # TODO: Come up with a proper detect() routine...and enable it. - return 1 - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/rpcgen.py b/scons/scons-local-2.2.0/SCons/Tool/rpcgen.py deleted file mode 100644 index c1542dc55..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/rpcgen.py +++ /dev/null @@ -1,70 +0,0 @@ -"""SCons.Tool.rpcgen - -Tool-specific initialization for RPCGEN tools. - -Three normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/rpcgen.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -from SCons.Builder import Builder -import SCons.Util - -cmd = "cd ${SOURCE.dir} && $RPCGEN -%s $RPCGENFLAGS %s -o ${TARGET.abspath} ${SOURCE.file}" - -rpcgen_client = cmd % ('l', '$RPCGENCLIENTFLAGS') -rpcgen_header = cmd % ('h', '$RPCGENHEADERFLAGS') -rpcgen_service = cmd % ('m', '$RPCGENSERVICEFLAGS') -rpcgen_xdr = cmd % ('c', '$RPCGENXDRFLAGS') - -def generate(env): - "Add RPCGEN Builders and construction variables for an Environment." - - client = Builder(action=rpcgen_client, suffix='_clnt.c', src_suffix='.x') - header = Builder(action=rpcgen_header, suffix='.h', src_suffix='.x') - service = Builder(action=rpcgen_service, suffix='_svc.c', src_suffix='.x') - xdr = Builder(action=rpcgen_xdr, suffix='_xdr.c', src_suffix='.x') - env.Append(BUILDERS={'RPCGenClient' : client, - 'RPCGenHeader' : header, - 'RPCGenService' : service, - 'RPCGenXDR' : xdr}) - env['RPCGEN'] = 'rpcgen' - env['RPCGENFLAGS'] = SCons.Util.CLVar('') - env['RPCGENCLIENTFLAGS'] = SCons.Util.CLVar('') - env['RPCGENHEADERFLAGS'] = SCons.Util.CLVar('') - env['RPCGENSERVICEFLAGS'] = SCons.Util.CLVar('') - env['RPCGENXDRFLAGS'] = SCons.Util.CLVar('') - -def exists(env): - return env.Detect('rpcgen') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/rpm.py b/scons/scons-local-2.2.0/SCons/Tool/rpm.py deleted file mode 100644 index 3a4d6a928..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/rpm.py +++ /dev/null @@ -1,132 +0,0 @@ -"""SCons.Tool.rpm - -Tool-specific initialization for rpm. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -The rpm tool calls the rpmbuild command. The first and only argument should a -tar.gz consisting of the source file and a specfile. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/rpm.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os -import re -import shutil -import subprocess - -import SCons.Builder -import SCons.Node.FS -import SCons.Util -import SCons.Action -import SCons.Defaults - -def get_cmd(source, env): - tar_file_with_included_specfile = source - if SCons.Util.is_List(source): - tar_file_with_included_specfile = source[0] - return "%s %s %s"%(env['RPM'], env['RPMFLAGS'], - tar_file_with_included_specfile.abspath ) - -def build_rpm(target, source, env): - # create a temporary rpm build root. - tmpdir = os.path.join( os.path.dirname( target[0].abspath ), 'rpmtemp' ) - if os.path.exists(tmpdir): - shutil.rmtree(tmpdir) - - # now create the mandatory rpm directory structure. - for d in ['RPMS', 'SRPMS', 'SPECS', 'BUILD']: - os.makedirs( os.path.join( tmpdir, d ) ) - - # set the topdir as an rpmflag. - env.Prepend( RPMFLAGS = '--define \'_topdir %s\'' % tmpdir ) - - # now call rpmbuild to create the rpm package. - handle = subprocess.Popen(get_cmd(source, env), - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - shell=True) - output = handle.stdout.read() - status = handle.wait() - - if status: - raise SCons.Errors.BuildError( node=target[0], - errstr=output, - filename=str(target[0]) ) - else: - # XXX: assume that LC_ALL=c is set while running rpmbuild - output_files = re.compile( 'Wrote: (.*)' ).findall( output ) - - for output, input in zip( output_files, target ): - rpm_output = os.path.basename(output) - expected = os.path.basename(input.get_path()) - - assert expected == rpm_output, "got %s but expected %s" % (rpm_output, expected) - shutil.copy( output, input.abspath ) - - - # cleanup before leaving. - shutil.rmtree(tmpdir) - - return status - -def string_rpm(target, source, env): - try: - return env['RPMCOMSTR'] - except KeyError: - return get_cmd(source, env) - -rpmAction = SCons.Action.Action(build_rpm, string_rpm) - -RpmBuilder = SCons.Builder.Builder(action = SCons.Action.Action('$RPMCOM', '$RPMCOMSTR'), - source_scanner = SCons.Defaults.DirScanner, - suffix = '$RPMSUFFIX') - - - -def generate(env): - """Add Builders and construction variables for rpm to an Environment.""" - try: - bld = env['BUILDERS']['Rpm'] - except KeyError: - bld = RpmBuilder - env['BUILDERS']['Rpm'] = bld - - env.SetDefault(RPM = 'LC_ALL=c rpmbuild') - env.SetDefault(RPMFLAGS = SCons.Util.CLVar('-ta')) - env.SetDefault(RPMCOM = rpmAction) - env.SetDefault(RPMSUFFIX = '.rpm') - -def exists(env): - return env.Detect('rpmbuild') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/sgiar.py b/scons/scons-local-2.2.0/SCons/Tool/sgiar.py deleted file mode 100644 index 42d07a2ed..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/sgiar.py +++ /dev/null @@ -1,68 +0,0 @@ -"""SCons.Tool.sgiar - -Tool-specific initialization for SGI ar (library archive). If CC -exists, static libraries should be built with it, so the prelinker has -a chance to resolve C++ template instantiations. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/sgiar.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Defaults -import SCons.Tool -import SCons.Util - -def generate(env): - """Add Builders and construction variables for ar to an Environment.""" - SCons.Tool.createStaticLibBuilder(env) - - if env.Detect('CC'): - env['AR'] = 'CC' - env['ARFLAGS'] = SCons.Util.CLVar('-ar') - env['ARCOM'] = '$AR $ARFLAGS -o $TARGET $SOURCES' - else: - env['AR'] = 'ar' - env['ARFLAGS'] = SCons.Util.CLVar('r') - env['ARCOM'] = '$AR $ARFLAGS $TARGET $SOURCES' - - env['SHLINK'] = '$LINK' - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') - env['SHLINKCOM'] = '$SHLINK $SHLINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' - env['LIBPREFIX'] = 'lib' - env['LIBSUFFIX'] = '.a' - -def exists(env): - return env.Detect('CC') or env.Detect('ar') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/sgic++.py b/scons/scons-local-2.2.0/SCons/Tool/sgic++.py deleted file mode 100644 index 5358c4b66..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/sgic++.py +++ /dev/null @@ -1,58 +0,0 @@ -"""SCons.Tool.sgic++ - -Tool-specific initialization for MIPSpro C++ on SGI. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/sgic++.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Util - -cplusplus = __import__('c++', globals(), locals(), []) - -def generate(env): - """Add Builders and construction variables for SGI MIPS C++ to an Environment.""" - - cplusplus.generate(env) - - env['CXX'] = 'CC' - env['CXXFLAGS'] = SCons.Util.CLVar('-LANG:std') - env['SHCXX'] = '$CXX' - env['SHOBJSUFFIX'] = '.o' - env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 - -def exists(env): - return env.Detect('CC') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/sgicc.py b/scons/scons-local-2.2.0/SCons/Tool/sgicc.py deleted file mode 100644 index c69d4fc9e..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/sgicc.py +++ /dev/null @@ -1,53 +0,0 @@ -"""SCons.Tool.sgicc - -Tool-specific initialization for MIPSPro cc on SGI. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/sgicc.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import cc - -def generate(env): - """Add Builders and construction variables for gcc to an Environment.""" - cc.generate(env) - - env['CXX'] = 'CC' - env['SHOBJSUFFIX'] = '.o' - env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 - -def exists(env): - return env.Detect('cc') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/sgilink.py b/scons/scons-local-2.2.0/SCons/Tool/sgilink.py deleted file mode 100644 index f65144697..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/sgilink.py +++ /dev/null @@ -1,62 +0,0 @@ -"""SCons.Tool.sgilink - -Tool-specific initialization for the SGI MIPSPro linker on SGI. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/sgilink.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Util - -import link - -linkers = ['CC', 'cc'] - -def generate(env): - """Add Builders and construction variables for MIPSPro to an Environment.""" - link.generate(env) - - env['LINK'] = env.Detect(linkers) or 'cc' - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') - - # __RPATH is set to $_RPATH in the platform specification if that - # platform supports it. - env['RPATHPREFIX'] = '-rpath ' - env['RPATHSUFFIX'] = '' - env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}' - -def exists(env): - return env.Detect(linkers) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/sunar.py b/scons/scons-local-2.2.0/SCons/Tool/sunar.py deleted file mode 100644 index 6e5f235f7..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/sunar.py +++ /dev/null @@ -1,67 +0,0 @@ -"""engine.SCons.Tool.sunar - -Tool-specific initialization for Solaris (Forte) ar (library archive). If CC -exists, static libraries should be built with it, so that template -instantians can be resolved. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/sunar.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Defaults -import SCons.Tool -import SCons.Util - -def generate(env): - """Add Builders and construction variables for ar to an Environment.""" - SCons.Tool.createStaticLibBuilder(env) - - if env.Detect('CC'): - env['AR'] = 'CC' - env['ARFLAGS'] = SCons.Util.CLVar('-xar') - env['ARCOM'] = '$AR $ARFLAGS -o $TARGET $SOURCES' - else: - env['AR'] = 'ar' - env['ARFLAGS'] = SCons.Util.CLVar('r') - env['ARCOM'] = '$AR $ARFLAGS $TARGET $SOURCES' - - env['SHLINK'] = '$LINK' - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -G') - env['SHLINKCOM'] = '$SHLINK $SHLINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' - env['LIBPREFIX'] = 'lib' - env['LIBSUFFIX'] = '.a' - -def exists(env): - return env.Detect('CC') or env.Detect('ar') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/sunc++.py b/scons/scons-local-2.2.0/SCons/Tool/sunc++.py deleted file mode 100644 index 6effe32f8..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/sunc++.py +++ /dev/null @@ -1,142 +0,0 @@ -"""SCons.Tool.sunc++ - -Tool-specific initialization for C++ on SunOS / Solaris. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/sunc++.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons - -import os -import re -import subprocess - -cplusplus = __import__('c++', globals(), locals(), []) - -package_info = {} - -def get_package_info(package_name, pkginfo, pkgchk): - try: - return package_info[package_name] - except KeyError: - version = None - pathname = None - try: - sadm_contents = open('/var/sadm/install/contents', 'r').read() - except EnvironmentError: - pass - else: - sadm_re = re.compile('^(\S*/bin/CC)(=\S*)? %s$' % package_name, re.M) - sadm_match = sadm_re.search(sadm_contents) - if sadm_match: - pathname = os.path.dirname(sadm_match.group(1)) - - try: - p = subprocess.Popen([pkginfo, '-l', package_name], - stdout=subprocess.PIPE, - stderr=open('/dev/null', 'w')) - except EnvironmentError: - pass - else: - pkginfo_contents = p.communicate()[0] - version_re = re.compile('^ *VERSION:\s*(.*)$', re.M) - version_match = version_re.search(pkginfo_contents) - if version_match: - version = version_match.group(1) - - if pathname is None: - try: - p = subprocess.Popen([pkgchk, '-l', package_name], - stdout=subprocess.PIPE, - stderr=open('/dev/null', 'w')) - except EnvironmentError: - pass - else: - pkgchk_contents = p.communicate()[0] - pathname_re = re.compile(r'^Pathname:\s*(.*/bin/CC)$', re.M) - pathname_match = pathname_re.search(pkgchk_contents) - if pathname_match: - pathname = os.path.dirname(pathname_match.group(1)) - - package_info[package_name] = (pathname, version) - return package_info[package_name] - -# use the package installer tool lslpp to figure out where cppc and what -# version of it is installed -def get_cppc(env): - cxx = env.subst('$CXX') - if cxx: - cppcPath = os.path.dirname(cxx) - else: - cppcPath = None - - cppcVersion = None - - pkginfo = env.subst('$PKGINFO') - pkgchk = env.subst('$PKGCHK') - - for package in ['SPROcpl']: - path, version = get_package_info(package, pkginfo, pkgchk) - if path and version: - cppcPath, cppcVersion = path, version - break - - return (cppcPath, 'CC', 'CC', cppcVersion) - -def generate(env): - """Add Builders and construction variables for SunPRO C++.""" - path, cxx, shcxx, version = get_cppc(env) - if path: - cxx = os.path.join(path, cxx) - shcxx = os.path.join(path, shcxx) - - cplusplus.generate(env) - - env['CXX'] = cxx - env['SHCXX'] = shcxx - env['CXXVERSION'] = version - env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -KPIC') - env['SHOBJPREFIX'] = 'so_' - env['SHOBJSUFFIX'] = '.o' - -def exists(env): - path, cxx, shcxx, version = get_cppc(env) - if path and cxx: - cppc = os.path.join(path, cxx) - if os.path.exists(cppc): - return cppc - return None - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/suncc.py b/scons/scons-local-2.2.0/SCons/Tool/suncc.py deleted file mode 100644 index 6b1461f85..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/suncc.py +++ /dev/null @@ -1,58 +0,0 @@ -"""SCons.Tool.suncc - -Tool-specific initialization for Sun Solaris (Forte) CC and cc. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/suncc.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Util - -import cc - -def generate(env): - """ - Add Builders and construction variables for Forte C and C++ compilers - to an Environment. - """ - cc.generate(env) - - env['CXX'] = 'CC' - env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS -KPIC') - env['SHOBJPREFIX'] = 'so_' - env['SHOBJSUFFIX'] = '.o' - -def exists(env): - return env.Detect('CC') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/sunf77.py b/scons/scons-local-2.2.0/SCons/Tool/sunf77.py deleted file mode 100644 index 1536c7126..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/sunf77.py +++ /dev/null @@ -1,63 +0,0 @@ -"""SCons.Tool.sunf77 - -Tool-specific initialization for sunf77, the Sun Studio F77 compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/sunf77.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Util - -from FortranCommon import add_all_to_env - -compilers = ['sunf77', 'f77'] - -def generate(env): - """Add Builders and construction variables for sunf77 to an Environment.""" - add_all_to_env(env) - - fcomp = env.Detect(compilers) or 'f77' - env['FORTRAN'] = fcomp - env['F77'] = fcomp - - env['SHFORTRAN'] = '$FORTRAN' - env['SHF77'] = '$F77' - - env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -KPIC') - env['SHF77FLAGS'] = SCons.Util.CLVar('$F77FLAGS -KPIC') - -def exists(env): - return env.Detect(compilers) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/sunf90.py b/scons/scons-local-2.2.0/SCons/Tool/sunf90.py deleted file mode 100644 index 65417f153..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/sunf90.py +++ /dev/null @@ -1,64 +0,0 @@ -"""SCons.Tool.sunf90 - -Tool-specific initialization for sunf90, the Sun Studio F90 compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/sunf90.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Util - -from FortranCommon import add_all_to_env - -compilers = ['sunf90', 'f90'] - -def generate(env): - """Add Builders and construction variables for sun f90 compiler to an - Environment.""" - add_all_to_env(env) - - fcomp = env.Detect(compilers) or 'f90' - env['FORTRAN'] = fcomp - env['F90'] = fcomp - - env['SHFORTRAN'] = '$FORTRAN' - env['SHF90'] = '$F90' - - env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -KPIC') - env['SHF90FLAGS'] = SCons.Util.CLVar('$F90FLAGS -KPIC') - -def exists(env): - return env.Detect(compilers) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/sunf95.py b/scons/scons-local-2.2.0/SCons/Tool/sunf95.py deleted file mode 100644 index c5300ad3b..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/sunf95.py +++ /dev/null @@ -1,64 +0,0 @@ -"""SCons.Tool.sunf95 - -Tool-specific initialization for sunf95, the Sun Studio F95 compiler. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/sunf95.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Util - -from FortranCommon import add_all_to_env - -compilers = ['sunf95', 'f95'] - -def generate(env): - """Add Builders and construction variables for sunf95 to an - Environment.""" - add_all_to_env(env) - - fcomp = env.Detect(compilers) or 'f95' - env['FORTRAN'] = fcomp - env['F95'] = fcomp - - env['SHFORTRAN'] = '$FORTRAN' - env['SHF95'] = '$F95' - - env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -KPIC') - env['SHF95FLAGS'] = SCons.Util.CLVar('$F95FLAGS -KPIC') - -def exists(env): - return env.Detect(compilers) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/sunlink.py b/scons/scons-local-2.2.0/SCons/Tool/sunlink.py deleted file mode 100644 index b747c8f2e..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/sunlink.py +++ /dev/null @@ -1,76 +0,0 @@ -"""SCons.Tool.sunlink - -Tool-specific initialization for the Sun Solaris (Forte) linker. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/sunlink.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os -import os.path - -import SCons.Util - -import link - -ccLinker = None - -# search for the acc compiler and linker front end - -try: - dirs = os.listdir('/opt') -except (IOError, OSError): - # Not being able to read the directory because it doesn't exist - # (IOError) or isn't readable (OSError) is okay. - dirs = [] - -for d in dirs: - linker = '/opt/' + d + '/bin/CC' - if os.path.exists(linker): - ccLinker = linker - break - -def generate(env): - """Add Builders and construction variables for Forte to an Environment.""" - link.generate(env) - - env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -G') - - env['RPATHPREFIX'] = '-R' - env['RPATHSUFFIX'] = '' - env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}' - -def exists(env): - return ccLinker - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/swig.py b/scons/scons-local-2.2.0/SCons/Tool/swig.py deleted file mode 100644 index 9f2a3800d..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/swig.py +++ /dev/null @@ -1,183 +0,0 @@ -"""SCons.Tool.swig - -Tool-specific initialization for swig. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/swig.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os.path -import re -import subprocess - -import SCons.Action -import SCons.Defaults -import SCons.Scanner -import SCons.Tool -import SCons.Util - -SwigAction = SCons.Action.Action('$SWIGCOM', '$SWIGCOMSTR') - -def swigSuffixEmitter(env, source): - if '-c++' in SCons.Util.CLVar(env.subst("$SWIGFLAGS", source=source)): - return '$SWIGCXXFILESUFFIX' - else: - return '$SWIGCFILESUFFIX' - -# Match '%module test', as well as '%module(directors="1") test' -# Also allow for test to be quoted (SWIG permits double quotes, but not single) -# Also allow for the line to have spaces after test if not quoted -_reModule = re.compile(r'%module(\s*\(.*\))?\s+("?)(\S+)\2') - -def _find_modules(src): - """Find all modules referenced by %module lines in `src`, a SWIG .i file. - Returns a list of all modules, and a flag set if SWIG directors have - been requested (SWIG will generate an additional header file in this - case.)""" - directors = 0 - mnames = [] - try: - matches = _reModule.findall(open(src).read()) - except IOError: - # If the file's not yet generated, guess the module name from the file stem - matches = [] - mnames.append(os.path.splitext(os.path.basename(src))[0]) - - for m in matches: - mnames.append(m[2]) - directors = directors or m[0].find('directors') >= 0 - return mnames, directors - -def _add_director_header_targets(target, env): - # Directors only work with C++ code, not C - suffix = env.subst(env['SWIGCXXFILESUFFIX']) - # For each file ending in SWIGCXXFILESUFFIX, add a new target director - # header by replacing the ending with SWIGDIRECTORSUFFIX. - for x in target[:]: - n = x.name - d = x.dir - if n[-len(suffix):] == suffix: - target.append(d.File(n[:-len(suffix)] + env['SWIGDIRECTORSUFFIX'])) - -def _swigEmitter(target, source, env): - swigflags = env.subst("$SWIGFLAGS", target=target, source=source) - flags = SCons.Util.CLVar(swigflags) - for src in source: - src = str(src.rfile()) - mnames = None - if "-python" in flags and "-noproxy" not in flags: - if mnames is None: - mnames, directors = _find_modules(src) - if directors: - _add_director_header_targets(target, env) - python_files = [m + ".py" for m in mnames] - outdir = env.subst('$SWIGOUTDIR', target=target, source=source) - # .py files should be generated in SWIGOUTDIR if specified, - # otherwise in the same directory as the target - if outdir: - python_files = [env.fs.File(os.path.join(outdir, j)) for j in python_files] - else: - python_files = [target[0].dir.File(m) for m in python_files] - target.extend(python_files) - if "-java" in flags: - if mnames is None: - mnames, directors = _find_modules(src) - if directors: - _add_director_header_targets(target, env) - java_files = [[m + ".java", m + "JNI.java"] for m in mnames] - java_files = SCons.Util.flatten(java_files) - outdir = env.subst('$SWIGOUTDIR', target=target, source=source) - if outdir: - java_files = [os.path.join(outdir, j) for j in java_files] - java_files = list(map(env.fs.File, java_files)) - for jf in java_files: - t_from_s = lambda t, p, s, x: t.dir - SCons.Util.AddMethod(jf, t_from_s, 'target_from_source') - target.extend(java_files) - return (target, source) - -def _get_swig_version(env): - """Run the SWIG command line tool to get and return the version number""" - pipe = SCons.Action._subproc(env, [env['SWIG'], '-version'], - stdin = 'devnull', - stderr = 'devnull', - stdout = subprocess.PIPE) - if pipe.wait() != 0: return - - out = pipe.stdout.read() - match = re.search(r'SWIG Version\s+(\S+)$', out, re.MULTILINE) - if match: - return match.group(1) - -def generate(env): - """Add Builders and construction variables for swig to an Environment.""" - c_file, cxx_file = SCons.Tool.createCFileBuilders(env) - - c_file.suffix['.i'] = swigSuffixEmitter - cxx_file.suffix['.i'] = swigSuffixEmitter - - c_file.add_action('.i', SwigAction) - c_file.add_emitter('.i', _swigEmitter) - cxx_file.add_action('.i', SwigAction) - cxx_file.add_emitter('.i', _swigEmitter) - - java_file = SCons.Tool.CreateJavaFileBuilder(env) - - java_file.suffix['.i'] = swigSuffixEmitter - - java_file.add_action('.i', SwigAction) - java_file.add_emitter('.i', _swigEmitter) - - env['SWIG'] = 'swig' - env['SWIGVERSION'] = _get_swig_version(env) - env['SWIGFLAGS'] = SCons.Util.CLVar('') - env['SWIGDIRECTORSUFFIX'] = '_wrap.h' - env['SWIGCFILESUFFIX'] = '_wrap$CFILESUFFIX' - env['SWIGCXXFILESUFFIX'] = '_wrap$CXXFILESUFFIX' - env['_SWIGOUTDIR'] = r'${"-outdir \"%s\"" % SWIGOUTDIR}' - env['SWIGPATH'] = [] - env['SWIGINCPREFIX'] = '-I' - env['SWIGINCSUFFIX'] = '' - env['_SWIGINCFLAGS'] = '$( ${_concat(SWIGINCPREFIX, SWIGPATH, SWIGINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' - env['SWIGCOM'] = '$SWIG -o $TARGET ${_SWIGOUTDIR} ${_SWIGINCFLAGS} $SWIGFLAGS $SOURCES' - - expr = '^[ \t]*%[ \t]*(?:include|import|extern)[ \t]*(<|"?)([^>\s"]+)(?:>|"?)' - scanner = SCons.Scanner.ClassicCPP("SWIGScan", ".i", "SWIGPATH", expr) - - env.Append(SCANNERS = scanner) - -def exists(env): - return env.Detect(['swig']) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/tar.py b/scons/scons-local-2.2.0/SCons/Tool/tar.py deleted file mode 100644 index 7cb9836b4..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/tar.py +++ /dev/null @@ -1,73 +0,0 @@ -"""SCons.Tool.tar - -Tool-specific initialization for tar. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/tar.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Action -import SCons.Builder -import SCons.Defaults -import SCons.Node.FS -import SCons.Util - -tars = ['tar', 'gtar'] - -TarAction = SCons.Action.Action('$TARCOM', '$TARCOMSTR') - -TarBuilder = SCons.Builder.Builder(action = TarAction, - source_factory = SCons.Node.FS.Entry, - source_scanner = SCons.Defaults.DirScanner, - suffix = '$TARSUFFIX', - multi = 1) - - -def generate(env): - """Add Builders and construction variables for tar to an Environment.""" - try: - bld = env['BUILDERS']['Tar'] - except KeyError: - bld = TarBuilder - env['BUILDERS']['Tar'] = bld - - env['TAR'] = env.Detect(tars) or 'gtar' - env['TARFLAGS'] = SCons.Util.CLVar('-c') - env['TARCOM'] = '$TAR $TARFLAGS -f $TARGET $SOURCES' - env['TARSUFFIX'] = '.tar' - -def exists(env): - return env.Detect(tars) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/tex.py b/scons/scons-local-2.2.0/SCons/Tool/tex.py deleted file mode 100644 index ce394e4f5..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/tex.py +++ /dev/null @@ -1,928 +0,0 @@ -"""SCons.Tool.tex - -Tool-specific initialization for TeX. -Generates .dvi files from .tex files - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/tex.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os.path -import re -import shutil -import sys -import platform -import glob - -import SCons.Action -import SCons.Node -import SCons.Node.FS -import SCons.Util -import SCons.Scanner.LaTeX - -Verbose = False - -must_rerun_latex = True - -# these are files that just need to be checked for changes and then rerun latex -check_suffixes = ['.toc', '.lof', '.lot', '.out', '.nav', '.snm'] - -# these are files that require bibtex or makeindex to be run when they change -all_suffixes = check_suffixes + ['.bbl', '.idx', '.nlo', '.glo', '.acn', '.bcf'] - -# -# regular expressions used to search for Latex features -# or outputs that require rerunning latex -# -# search for all .aux files opened by latex (recorded in the .fls file) -openout_aux_re = re.compile(r"OUTPUT *(.*\.aux)") - -# search for all .bcf files opened by latex (recorded in the .fls file) -# for use by biber -openout_bcf_re = re.compile(r"OUTPUT *(.*\.bcf)") - -#printindex_re = re.compile(r"^[^%]*\\printindex", re.MULTILINE) -#printnomenclature_re = re.compile(r"^[^%]*\\printnomenclature", re.MULTILINE) -#printglossary_re = re.compile(r"^[^%]*\\printglossary", re.MULTILINE) - -# search to find rerun warnings -warning_rerun_str = '(^LaTeX Warning:.*Rerun)|(^Package \w+ Warning:.*Rerun)' -warning_rerun_re = re.compile(warning_rerun_str, re.MULTILINE) - -# search to find citation rerun warnings -rerun_citations_str = "^LaTeX Warning:.*\n.*Rerun to get citations correct" -rerun_citations_re = re.compile(rerun_citations_str, re.MULTILINE) - -# search to find undefined references or citations warnings -undefined_references_str = '(^LaTeX Warning:.*undefined references)|(^Package \w+ Warning:.*undefined citations)' -undefined_references_re = re.compile(undefined_references_str, re.MULTILINE) - -# used by the emitter -auxfile_re = re.compile(r".", re.MULTILINE) -tableofcontents_re = re.compile(r"^[^%\n]*\\tableofcontents", re.MULTILINE) -makeindex_re = re.compile(r"^[^%\n]*\\makeindex", re.MULTILINE) -bibliography_re = re.compile(r"^[^%\n]*\\bibliography", re.MULTILINE) -bibunit_re = re.compile(r"^[^%\n]*\\begin\{bibunit\}", re.MULTILINE) -multibib_re = re.compile(r"^[^%\n]*\\newcites\{([^\}]*)\}", re.MULTILINE) -addbibresource_re = re.compile(r"^[^%\n]*\\(addbibresource|addglobalbib|addsectionbib)", re.MULTILINE) -listoffigures_re = re.compile(r"^[^%\n]*\\listoffigures", re.MULTILINE) -listoftables_re = re.compile(r"^[^%\n]*\\listoftables", re.MULTILINE) -hyperref_re = re.compile(r"^[^%\n]*\\usepackage.*\{hyperref\}", re.MULTILINE) -makenomenclature_re = re.compile(r"^[^%\n]*\\makenomenclature", re.MULTILINE) -makeglossary_re = re.compile(r"^[^%\n]*\\makeglossary", re.MULTILINE) -makeglossaries_re = re.compile(r"^[^%\n]*\\makeglossaries", re.MULTILINE) -makeacronyms_re = re.compile(r"^[^%\n]*\\makeglossaries", re.MULTILINE) -beamer_re = re.compile(r"^[^%\n]*\\documentclass\{beamer\}", re.MULTILINE) - -# search to find all files included by Latex -include_re = re.compile(r'^[^%\n]*\\(?:include|input){([^}]*)}', re.MULTILINE) -includeOnly_re = re.compile(r'^[^%\n]*\\(?:include){([^}]*)}', re.MULTILINE) - -# search to find all graphics files included by Latex -includegraphics_re = re.compile(r'^[^%\n]*\\(?:includegraphics(?:\[[^\]]+\])?){([^}]*)}', re.MULTILINE) - -# search to find all files opened by Latex (recorded in .log file) -openout_re = re.compile(r"OUTPUT *(.*)") - -# list of graphics file extensions for TeX and LaTeX -TexGraphics = SCons.Scanner.LaTeX.TexGraphics -LatexGraphics = SCons.Scanner.LaTeX.LatexGraphics - -# An Action sufficient to build any generic tex file. -TeXAction = None - -# An action to build a latex file. This action might be needed more -# than once if we are dealing with labels and bibtex. -LaTeXAction = None - -# An action to run BibTeX on a file. -BibTeXAction = None - -# An action to run MakeIndex on a file. -MakeIndexAction = None - -# An action to run MakeIndex (for nomencl) on a file. -MakeNclAction = None - -# An action to run MakeIndex (for glossary) on a file. -MakeGlossaryAction = None - -# An action to run MakeIndex (for acronyms) on a file. -MakeAcronymsAction = None - -# Used as a return value of modify_env_var if the variable is not set. -_null = SCons.Scanner.LaTeX._null - -modify_env_var = SCons.Scanner.LaTeX.modify_env_var - -def check_file_error_message(utility, filename='log'): - msg = '%s returned an error, check the %s file\n' % (utility, filename) - sys.stdout.write(msg) - -def FindFile(name,suffixes,paths,env,requireExt=False): - if requireExt: - name,ext = SCons.Util.splitext(name) - # if the user gave an extension use it. - if ext: - name = name + ext - if Verbose: - print " searching for '%s' with extensions: " % name,suffixes - - for path in paths: - testName = os.path.join(path,name) - if Verbose: - print " look for '%s'" % testName - if os.path.isfile(testName): - if Verbose: - print " found '%s'" % testName - return env.fs.File(testName) - else: - name_ext = SCons.Util.splitext(testName)[1] - if name_ext: - continue - - # if no suffix try adding those passed in - for suffix in suffixes: - testNameExt = testName + suffix - if Verbose: - print " look for '%s'" % testNameExt - - if os.path.isfile(testNameExt): - if Verbose: - print " found '%s'" % testNameExt - return env.fs.File(testNameExt) - if Verbose: - print " did not find '%s'" % name - return None - -def InternalLaTeXAuxAction(XXXLaTeXAction, target = None, source= None, env=None): - """A builder for LaTeX files that checks the output in the aux file - and decides how many times to use LaTeXAction, and BibTeXAction.""" - - global must_rerun_latex - - # This routine is called with two actions. In this file for DVI builds - # with LaTeXAction and from the pdflatex.py with PDFLaTeXAction - # set this up now for the case where the user requests a different extension - # for the target filename - if (XXXLaTeXAction == LaTeXAction): - callerSuffix = ".dvi" - else: - callerSuffix = env['PDFSUFFIX'] - - basename = SCons.Util.splitext(str(source[0]))[0] - basedir = os.path.split(str(source[0]))[0] - basefile = os.path.split(str(basename))[1] - abspath = os.path.abspath(basedir) - - targetext = os.path.splitext(str(target[0]))[1] - targetdir = os.path.split(str(target[0]))[0] - - saved_env = {} - for var in SCons.Scanner.LaTeX.LaTeX.env_variables: - saved_env[var] = modify_env_var(env, var, abspath) - - # Create base file names with the target directory since the auxiliary files - # will be made there. That's because the *COM variables have the cd - # command in the prolog. We check - # for the existence of files before opening them--even ones like the - # aux file that TeX always creates--to make it possible to write tests - # with stubs that don't necessarily generate all of the same files. - - targetbase = os.path.join(targetdir, basefile) - - # if there is a \makeindex there will be a .idx and thus - # we have to run makeindex at least once to keep the build - # happy even if there is no index. - # Same for glossaries, nomenclature, and acronyms - src_content = source[0].get_text_contents() - run_makeindex = makeindex_re.search(src_content) and not os.path.isfile(targetbase + '.idx') - run_nomenclature = makenomenclature_re.search(src_content) and not os.path.isfile(targetbase + '.nlo') - run_glossary = makeglossary_re.search(src_content) and not os.path.isfile(targetbase + '.glo') - run_glossaries = makeglossaries_re.search(src_content) and not os.path.isfile(targetbase + '.glo') - run_acronyms = makeacronyms_re.search(src_content) and not os.path.isfile(targetbase + '.acn') - - saved_hashes = {} - suffix_nodes = {} - - for suffix in all_suffixes: - theNode = env.fs.File(targetbase + suffix) - suffix_nodes[suffix] = theNode - saved_hashes[suffix] = theNode.get_csig() - - if Verbose: - print "hashes: ",saved_hashes - - must_rerun_latex = True - - # .aux files already processed by BibTex - already_bibtexed = [] - - # - # routine to update MD5 hash and compare - # - def check_MD5(filenode, suffix): - global must_rerun_latex - # two calls to clear old csig - filenode.clear_memoized_values() - filenode.ninfo = filenode.new_ninfo() - new_md5 = filenode.get_csig() - - if saved_hashes[suffix] == new_md5: - if Verbose: - print "file %s not changed" % (targetbase+suffix) - return False # unchanged - saved_hashes[suffix] = new_md5 - must_rerun_latex = True - if Verbose: - print "file %s changed, rerunning Latex, new hash = " % (targetbase+suffix), new_md5 - return True # changed - - # generate the file name that latex will generate - resultfilename = targetbase + callerSuffix - - count = 0 - - while (must_rerun_latex and count < int(env.subst('$LATEXRETRIES'))) : - result = XXXLaTeXAction(target, source, env) - if result != 0: - return result - - count = count + 1 - - must_rerun_latex = False - # Decide if various things need to be run, or run again. - - # Read the log file to find warnings/errors - logfilename = targetbase + '.log' - logContent = '' - if os.path.isfile(logfilename): - logContent = open(logfilename, "rb").read() - - - # Read the fls file to find all .aux files - flsfilename = targetbase + '.fls' - flsContent = '' - auxfiles = [] - if os.path.isfile(flsfilename): - flsContent = open(flsfilename, "rb").read() - auxfiles = openout_aux_re.findall(flsContent) - # remove duplicates - dups = {} - for x in auxfiles: - dups[x] = 1 - auxfiles = list(dups.keys()) - - bcffiles = [] - if os.path.isfile(flsfilename): - flsContent = open(flsfilename, "rb").read() - bcffiles = openout_bcf_re.findall(flsContent) - # remove duplicates - dups = {} - for x in bcffiles: - dups[x] = 1 - bcffiles = list(dups.keys()) - - if Verbose: - print "auxfiles ",auxfiles - print "bcffiles ",bcffiles - - # Now decide if bibtex will need to be run. - # The information that bibtex reads from the .aux file is - # pass-independent. If we find (below) that the .bbl file is unchanged, - # then the last latex saw a correct bibliography. - # Therefore only do this once - # Go through all .aux files and remember the files already done. - for auxfilename in auxfiles: - if auxfilename not in already_bibtexed: - already_bibtexed.append(auxfilename) - target_aux = os.path.join(targetdir, auxfilename) - if os.path.isfile(target_aux): - content = open(target_aux, "rb").read() - if content.find("bibdata") != -1: - if Verbose: - print "Need to run bibtex on ",auxfilename - bibfile = env.fs.File(SCons.Util.splitext(target_aux)[0]) - result = BibTeXAction(bibfile, bibfile, env) - if result != 0: - check_file_error_message(env['BIBTEX'], 'blg') - must_rerun_latex = True - - # Now decide if biber will need to be run. - # The information that bibtex reads from the .bcf file is - # pass-independent. If we find (below) that the .bbl file is unchanged, - # then the last latex saw a correct bibliography. - # Therefore only do this once - # Go through all .bcf files and remember the files already done. - for bcffilename in bcffiles: - if bcffilename not in already_bibtexed: - already_bibtexed.append(bcffilename) - target_bcf = os.path.join(targetdir, bcffilename) - if os.path.isfile(target_bcf): - content = open(target_bcf, "rb").read() - if content.find("bibdata") != -1: - if Verbose: - print "Need to run bibtex on ",bcffilename - bibfile = env.fs.File(SCons.Util.splitext(target_bcf)[0]) - result = BibTeXAction(bibfile, bibfile, env) - if result != 0: - check_file_error_message(env['BIBTEX'], 'blg') - must_rerun_latex = True - - # Now decide if latex will need to be run again due to index. - if check_MD5(suffix_nodes['.idx'],'.idx') or (count == 1 and run_makeindex): - # We must run makeindex - if Verbose: - print "Need to run makeindex" - idxfile = suffix_nodes['.idx'] - result = MakeIndexAction(idxfile, idxfile, env) - if result != 0: - check_file_error_message(env['MAKEINDEX'], 'ilg') - return result - - # TO-DO: need to add a way for the user to extend this list for whatever - # auxiliary files they create in other (or their own) packages - # Harder is case is where an action needs to be called -- that should be rare (I hope?) - - for index in check_suffixes: - check_MD5(suffix_nodes[index],index) - - # Now decide if latex will need to be run again due to nomenclature. - if check_MD5(suffix_nodes['.nlo'],'.nlo') or (count == 1 and run_nomenclature): - # We must run makeindex - if Verbose: - print "Need to run makeindex for nomenclature" - nclfile = suffix_nodes['.nlo'] - result = MakeNclAction(nclfile, nclfile, env) - if result != 0: - check_file_error_message('%s (nomenclature)' % env['MAKENCL'], - 'nlg') - #return result - - # Now decide if latex will need to be run again due to glossary. - if check_MD5(suffix_nodes['.glo'],'.glo') or (count == 1 and run_glossaries) or (count == 1 and run_glossary): - # We must run makeindex - if Verbose: - print "Need to run makeindex for glossary" - glofile = suffix_nodes['.glo'] - result = MakeGlossaryAction(glofile, glofile, env) - if result != 0: - check_file_error_message('%s (glossary)' % env['MAKEGLOSSARY'], - 'glg') - #return result - - # Now decide if latex will need to be run again due to acronyms. - if check_MD5(suffix_nodes['.acn'],'.acn') or (count == 1 and run_acronyms): - # We must run makeindex - if Verbose: - print "Need to run makeindex for acronyms" - acrfile = suffix_nodes['.acn'] - result = MakeAcronymsAction(acrfile, acrfile, env) - if result != 0: - check_file_error_message('%s (acronyms)' % env['MAKEACRONYMS'], - 'alg') - return result - - # Now decide if latex needs to be run yet again to resolve warnings. - if warning_rerun_re.search(logContent): - must_rerun_latex = True - if Verbose: - print "rerun Latex due to latex or package rerun warning" - - if rerun_citations_re.search(logContent): - must_rerun_latex = True - if Verbose: - print "rerun Latex due to 'Rerun to get citations correct' warning" - - if undefined_references_re.search(logContent): - must_rerun_latex = True - if Verbose: - print "rerun Latex due to undefined references or citations" - - if (count >= int(env.subst('$LATEXRETRIES')) and must_rerun_latex): - print "reached max number of retries on Latex ,",int(env.subst('$LATEXRETRIES')) -# end of while loop - - # rename Latex's output to what the target name is - if not (str(target[0]) == resultfilename and os.path.isfile(resultfilename)): - if os.path.isfile(resultfilename): - print "move %s to %s" % (resultfilename, str(target[0]), ) - shutil.move(resultfilename,str(target[0])) - - # Original comment (when TEXPICTS was not restored): - # The TEXPICTS enviroment variable is needed by a dvi -> pdf step - # later on Mac OSX so leave it - # - # It is also used when searching for pictures (implicit dependencies). - # Why not set the variable again in the respective builder instead - # of leaving local modifications in the environment? What if multiple - # latex builds in different directories need different TEXPICTS? - for var in SCons.Scanner.LaTeX.LaTeX.env_variables: - if var == 'TEXPICTS': - continue - if saved_env[var] is _null: - try: - del env['ENV'][var] - except KeyError: - pass # was never set - else: - env['ENV'][var] = saved_env[var] - - return result - -def LaTeXAuxAction(target = None, source= None, env=None): - result = InternalLaTeXAuxAction( LaTeXAction, target, source, env ) - return result - -LaTeX_re = re.compile("\\\\document(style|class)") - -def is_LaTeX(flist,env,abspath): - """Scan a file list to decide if it's TeX- or LaTeX-flavored.""" - - # We need to scan files that are included in case the - # \documentclass command is in them. - - # get path list from both env['TEXINPUTS'] and env['ENV']['TEXINPUTS'] - savedpath = modify_env_var(env, 'TEXINPUTS', abspath) - paths = env['ENV']['TEXINPUTS'] - if SCons.Util.is_List(paths): - pass - else: - # Split at os.pathsep to convert into absolute path - paths = paths.split(os.pathsep) - - # now that we have the path list restore the env - if savedpath is _null: - try: - del env['ENV']['TEXINPUTS'] - except KeyError: - pass # was never set - else: - env['ENV']['TEXINPUTS'] = savedpath - if Verbose: - print "is_LaTeX search path ",paths - print "files to search :",flist - - # Now that we have the search path and file list, check each one - for f in flist: - if Verbose: - print " checking for Latex source ",str(f) - - content = f.get_text_contents() - if LaTeX_re.search(content): - if Verbose: - print "file %s is a LaTeX file" % str(f) - return 1 - if Verbose: - print "file %s is not a LaTeX file" % str(f) - - # now find included files - inc_files = [ ] - inc_files.extend( include_re.findall(content) ) - if Verbose: - print "files included by '%s': "%str(f),inc_files - # inc_files is list of file names as given. need to find them - # using TEXINPUTS paths. - - # search the included files - for src in inc_files: - srcNode = FindFile(src,['.tex','.ltx','.latex'],paths,env,requireExt=False) - # make this a list since is_LaTeX takes a list. - fileList = [srcNode,] - if Verbose: - print "FindFile found ",srcNode - if srcNode is not None: - file_test = is_LaTeX(fileList, env, abspath) - - # return on first file that finds latex is needed. - if file_test: - return file_test - - if Verbose: - print " done scanning ",str(f) - - return 0 - -def TeXLaTeXFunction(target = None, source= None, env=None): - """A builder for TeX and LaTeX that scans the source file to - decide the "flavor" of the source and then executes the appropriate - program.""" - - # find these paths for use in is_LaTeX to search for included files - basedir = os.path.split(str(source[0]))[0] - abspath = os.path.abspath(basedir) - - if is_LaTeX(source,env,abspath): - result = LaTeXAuxAction(target,source,env) - if result != 0: - check_file_error_message(env['LATEX']) - else: - result = TeXAction(target,source,env) - if result != 0: - check_file_error_message(env['TEX']) - return result - -def TeXLaTeXStrFunction(target = None, source= None, env=None): - """A strfunction for TeX and LaTeX that scans the source file to - decide the "flavor" of the source and then returns the appropriate - command string.""" - if env.GetOption("no_exec"): - - # find these paths for use in is_LaTeX to search for included files - basedir = os.path.split(str(source[0]))[0] - abspath = os.path.abspath(basedir) - - if is_LaTeX(source,env,abspath): - result = env.subst('$LATEXCOM',0,target,source)+" ..." - else: - result = env.subst("$TEXCOM",0,target,source)+" ..." - else: - result = '' - return result - -def tex_eps_emitter(target, source, env): - """An emitter for TeX and LaTeX sources when - executing tex or latex. It will accept .ps and .eps - graphics files - """ - (target, source) = tex_emitter_core(target, source, env, TexGraphics) - - return (target, source) - -def tex_pdf_emitter(target, source, env): - """An emitter for TeX and LaTeX sources when - executing pdftex or pdflatex. It will accept graphics - files of types .pdf, .jpg, .png, .gif, and .tif - """ - (target, source) = tex_emitter_core(target, source, env, LatexGraphics) - - return (target, source) - -def ScanFiles(theFile, target, paths, file_tests, file_tests_search, env, graphics_extensions, targetdir, aux_files): - """ For theFile (a Node) update any file_tests and search for graphics files - then find all included files and call ScanFiles recursively for each of them""" - - content = theFile.get_text_contents() - if Verbose: - print " scanning ",str(theFile) - - for i in range(len(file_tests_search)): - if file_tests[i][0] is None: - file_tests[i][0] = file_tests_search[i].search(content) - if Verbose and file_tests[i][0]: - print " found match for ",file_tests[i][-1][-1] - - incResult = includeOnly_re.search(content) - if incResult: - aux_files.append(os.path.join(targetdir, incResult.group(1))) - if Verbose: - print "\include file names : ", aux_files - # recursively call this on each of the included files - inc_files = [ ] - inc_files.extend( include_re.findall(content) ) - if Verbose: - print "files included by '%s': "%str(theFile),inc_files - # inc_files is list of file names as given. need to find them - # using TEXINPUTS paths. - - for src in inc_files: - srcNode = FindFile(src,['.tex','.ltx','.latex'],paths,env,requireExt=False) - if srcNode is not None: - file_tests = ScanFiles(srcNode, target, paths, file_tests, file_tests_search, env, graphics_extensions, targetdir, aux_files) - if Verbose: - print " done scanning ",str(theFile) - return file_tests - -def tex_emitter_core(target, source, env, graphics_extensions): - """An emitter for TeX and LaTeX sources. - For LaTeX sources we try and find the common created files that - are needed on subsequent runs of latex to finish tables of contents, - bibliographies, indices, lists of figures, and hyperlink references. - """ - basename = SCons.Util.splitext(str(source[0]))[0] - basefile = os.path.split(str(basename))[1] - targetdir = os.path.split(str(target[0]))[0] - targetbase = os.path.join(targetdir, basefile) - - basedir = os.path.split(str(source[0]))[0] - abspath = os.path.abspath(basedir) - target[0].attributes.path = abspath - - # - # file names we will make use of in searching the sources and log file - # - emit_suffixes = ['.aux', '.log', '.ilg', '.blg', '.nls', '.nlg', '.gls', '.glg', '.alg'] + all_suffixes - auxfilename = targetbase + '.aux' - logfilename = targetbase + '.log' - flsfilename = targetbase + '.fls' - - env.SideEffect(auxfilename,target[0]) - env.SideEffect(logfilename,target[0]) - env.SideEffect(flsfilename,target[0]) - if Verbose: - print "side effect :",auxfilename,logfilename,flsfilename - env.Clean(target[0],auxfilename) - env.Clean(target[0],logfilename) - env.Clean(target[0],flsfilename) - - content = source[0].get_text_contents() - - # These variables are no longer used. - #idx_exists = os.path.isfile(targetbase + '.idx') - #nlo_exists = os.path.isfile(targetbase + '.nlo') - #glo_exists = os.path.isfile(targetbase + '.glo') - #acr_exists = os.path.isfile(targetbase + '.acn') - - # set up list with the regular expressions - # we use to find features used - file_tests_search = [auxfile_re, - makeindex_re, - bibliography_re, - bibunit_re, - multibib_re, - addbibresource_re, - tableofcontents_re, - listoffigures_re, - listoftables_re, - hyperref_re, - makenomenclature_re, - makeglossary_re, - makeglossaries_re, - makeacronyms_re, - beamer_re ] - # set up list with the file suffixes that need emitting - # when a feature is found - file_tests_suff = [['.aux','aux_file'], - ['.idx', '.ind', '.ilg','makeindex'], - ['.bbl', '.blg','bibliography'], - ['.bbl', '.blg','bibunit'], - ['.bbl', '.blg','multibib'], - ['.bbl', '.blg','.bcf','addbibresource'], - ['.toc','contents'], - ['.lof','figures'], - ['.lot','tables'], - ['.out','hyperref'], - ['.nlo', '.nls', '.nlg','nomenclature'], - ['.glo', '.gls', '.glg','glossary'], - ['.glo', '.gls', '.glg','glossaries'], - ['.acn', '.acr', '.alg','acronyms'], - ['.nav', '.snm', '.out', '.toc','beamer'] ] - # build the list of lists - file_tests = [] - for i in range(len(file_tests_search)): - file_tests.append( [None, file_tests_suff[i]] ) - - # TO-DO: need to add a way for the user to extend this list for whatever - # auxiliary files they create in other (or their own) packages - - # get path list from both env['TEXINPUTS'] and env['ENV']['TEXINPUTS'] - savedpath = modify_env_var(env, 'TEXINPUTS', abspath) - paths = env['ENV']['TEXINPUTS'] - if SCons.Util.is_List(paths): - pass - else: - # Split at os.pathsep to convert into absolute path - paths = paths.split(os.pathsep) - - # now that we have the path list restore the env - if savedpath is _null: - try: - del env['ENV']['TEXINPUTS'] - except KeyError: - pass # was never set - else: - env['ENV']['TEXINPUTS'] = savedpath - if Verbose: - print "search path ",paths - - aux_files = [] - file_tests = ScanFiles(source[0], target, paths, file_tests, file_tests_search, env, graphics_extensions, targetdir, aux_files) - - for (theSearch,suffix_list) in file_tests: - # add side effects if feature is present.If file is to be generated,add all side effects - if Verbose and theSearch: - print "check side effects for ",suffix_list[-1] - if (theSearch != None) or (not source[0].exists() ): - file_list = [targetbase,] - # for bibunit we need a list of files - if suffix_list[-1] == 'bibunit': - file_basename = os.path.join(targetdir, 'bu*.aux') - file_list = glob.glob(file_basename) - # remove the suffix '.aux' - for i in range(len(file_list)): - file_list.append(SCons.Util.splitext(file_list[i])[0]) - # for multibib we need a list of files - if suffix_list[-1] == 'multibib': - for multibibmatch in multibib_re.finditer(content): - if Verbose: - print "multibib match ",multibibmatch.group(1) - if multibibmatch != None: - baselist = multibibmatch.group(1).split(',') - if Verbose: - print "multibib list ", baselist - for i in range(len(baselist)): - file_list.append(os.path.join(targetdir, baselist[i])) - # now define the side effects - for file_name in file_list: - for suffix in suffix_list[:-1]: - env.SideEffect(file_name + suffix,target[0]) - if Verbose: - print "side effect tst :",file_name + suffix, " target is ",str(target[0]) - env.Clean(target[0],file_name + suffix) - - for aFile in aux_files: - aFile_base = SCons.Util.splitext(aFile)[0] - env.SideEffect(aFile_base + '.aux',target[0]) - if Verbose: - print "side effect aux :",aFile_base + '.aux' - env.Clean(target[0],aFile_base + '.aux') - # read fls file to get all other files that latex creates and will read on the next pass - # remove files from list that we explicitly dealt with above - if os.path.isfile(flsfilename): - content = open(flsfilename, "rb").read() - out_files = openout_re.findall(content) - myfiles = [auxfilename, logfilename, flsfilename, targetbase+'.dvi',targetbase+'.pdf'] - for filename in out_files[:]: - if filename in myfiles: - out_files.remove(filename) - env.SideEffect(out_files,target[0]) - if Verbose: - print "side effect fls :",out_files - env.Clean(target[0],out_files) - - return (target, source) - - -TeXLaTeXAction = None - -def generate(env): - """Add Builders and construction variables for TeX to an Environment.""" - - global TeXLaTeXAction - if TeXLaTeXAction is None: - TeXLaTeXAction = SCons.Action.Action(TeXLaTeXFunction, - strfunction=TeXLaTeXStrFunction) - - env.AppendUnique(LATEXSUFFIXES=SCons.Tool.LaTeXSuffixes) - - generate_common(env) - - import dvi - dvi.generate(env) - - bld = env['BUILDERS']['DVI'] - bld.add_action('.tex', TeXLaTeXAction) - bld.add_emitter('.tex', tex_eps_emitter) - -def generate_darwin(env): - try: - environ = env['ENV'] - except KeyError: - environ = {} - env['ENV'] = environ - - if (platform.system() == 'Darwin'): - try: - ospath = env['ENV']['PATHOSX'] - except: - ospath = None - if ospath: - env.AppendENVPath('PATH', ospath) - -def generate_common(env): - """Add internal Builders and construction variables for LaTeX to an Environment.""" - - # Add OSX system paths so TeX tools can be found - # when a list of tools is given the exists() method is not called - generate_darwin(env) - - # A generic tex file Action, sufficient for all tex files. - global TeXAction - if TeXAction is None: - TeXAction = SCons.Action.Action("$TEXCOM", "$TEXCOMSTR") - - # An Action to build a latex file. This might be needed more - # than once if we are dealing with labels and bibtex. - global LaTeXAction - if LaTeXAction is None: - LaTeXAction = SCons.Action.Action("$LATEXCOM", "$LATEXCOMSTR") - - # Define an action to run BibTeX on a file. - global BibTeXAction - if BibTeXAction is None: - BibTeXAction = SCons.Action.Action("$BIBTEXCOM", "$BIBTEXCOMSTR") - - # Define an action to run MakeIndex on a file. - global MakeIndexAction - if MakeIndexAction is None: - MakeIndexAction = SCons.Action.Action("$MAKEINDEXCOM", "$MAKEINDEXCOMSTR") - - # Define an action to run MakeIndex on a file for nomenclatures. - global MakeNclAction - if MakeNclAction is None: - MakeNclAction = SCons.Action.Action("$MAKENCLCOM", "$MAKENCLCOMSTR") - - # Define an action to run MakeIndex on a file for glossaries. - global MakeGlossaryAction - if MakeGlossaryAction is None: - MakeGlossaryAction = SCons.Action.Action("$MAKEGLOSSARYCOM", "$MAKEGLOSSARYCOMSTR") - - # Define an action to run MakeIndex on a file for acronyms. - global MakeAcronymsAction - if MakeAcronymsAction is None: - MakeAcronymsAction = SCons.Action.Action("$MAKEACRONYMSCOM", "$MAKEACRONYMSCOMSTR") - - try: - environ = env['ENV'] - except KeyError: - environ = {} - env['ENV'] = environ - - # Some Linux platforms have pdflatex set up in a way - # that requires that the HOME environment variable be set. - # Add it here if defined. - v = os.environ.get('HOME') - if v: - environ['HOME'] = v - - CDCOM = 'cd ' - if platform.system() == 'Windows': - # allow cd command to change drives on Windows - CDCOM = 'cd /D ' - - env['TEX'] = 'tex' - env['TEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode -recorder') - env['TEXCOM'] = CDCOM + '${TARGET.dir} && $TEX $TEXFLAGS ${SOURCE.file}' - - env['PDFTEX'] = 'pdftex' - env['PDFTEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode -recorder') - env['PDFTEXCOM'] = CDCOM + '${TARGET.dir} && $PDFTEX $PDFTEXFLAGS ${SOURCE.file}' - - env['LATEX'] = 'latex' - env['LATEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode -recorder') - env['LATEXCOM'] = CDCOM + '${TARGET.dir} && $LATEX $LATEXFLAGS ${SOURCE.file}' - env['LATEXRETRIES'] = 4 - - env['PDFLATEX'] = 'pdflatex' - env['PDFLATEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode -recorder') - env['PDFLATEXCOM'] = CDCOM + '${TARGET.dir} && $PDFLATEX $PDFLATEXFLAGS ${SOURCE.file}' - - env['BIBTEX'] = 'bibtex' - env['BIBTEXFLAGS'] = SCons.Util.CLVar('') - env['BIBTEXCOM'] = CDCOM + '${TARGET.dir} && $BIBTEX $BIBTEXFLAGS ${SOURCE.filebase}' - - env['MAKEINDEX'] = 'makeindex' - env['MAKEINDEXFLAGS'] = SCons.Util.CLVar('') - env['MAKEINDEXCOM'] = CDCOM + '${TARGET.dir} && $MAKEINDEX $MAKEINDEXFLAGS ${SOURCE.file}' - - env['MAKEGLOSSARY'] = 'makeindex' - env['MAKEGLOSSARYSTYLE'] = '${SOURCE.filebase}.ist' - env['MAKEGLOSSARYFLAGS'] = SCons.Util.CLVar('-s ${MAKEGLOSSARYSTYLE} -t ${SOURCE.filebase}.glg') - env['MAKEGLOSSARYCOM'] = CDCOM + '${TARGET.dir} && $MAKEGLOSSARY ${SOURCE.filebase}.glo $MAKEGLOSSARYFLAGS -o ${SOURCE.filebase}.gls' - - env['MAKEACRONYMS'] = 'makeindex' - env['MAKEACRONYMSSTYLE'] = '${SOURCE.filebase}.ist' - env['MAKEACRONYMSFLAGS'] = SCons.Util.CLVar('-s ${MAKEACRONYMSSTYLE} -t ${SOURCE.filebase}.alg') - env['MAKEACRONYMSCOM'] = CDCOM + '${TARGET.dir} && $MAKEACRONYMS ${SOURCE.filebase}.acn $MAKEACRONYMSFLAGS -o ${SOURCE.filebase}.acr' - - env['MAKENCL'] = 'makeindex' - env['MAKENCLSTYLE'] = 'nomencl.ist' - env['MAKENCLFLAGS'] = '-s ${MAKENCLSTYLE} -t ${SOURCE.filebase}.nlg' - env['MAKENCLCOM'] = CDCOM + '${TARGET.dir} && $MAKENCL ${SOURCE.filebase}.nlo $MAKENCLFLAGS -o ${SOURCE.filebase}.nls' - -def exists(env): - generate_darwin(env) - return env.Detect('tex') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/textfile.py b/scons/scons-local-2.2.0/SCons/Tool/textfile.py deleted file mode 100644 index 44fd99941..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/textfile.py +++ /dev/null @@ -1,175 +0,0 @@ -# -*- python -*- -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__doc__ = """ -Textfile/Substfile builder for SCons. - - Create file 'target' which typically is a textfile. The 'source' - may be any combination of strings, Nodes, or lists of same. A - 'linesep' will be put between any part written and defaults to - os.linesep. - - The only difference between the Textfile builder and the Substfile - builder is that strings are converted to Value() nodes for the - former and File() nodes for the latter. To insert files in the - former or strings in the latter, wrap them in a File() or Value(), - respectively. - - The values of SUBST_DICT first have any construction variables - expanded (its keys are not expanded). If a value of SUBST_DICT is - a python callable function, it is called and the result is expanded - as the value. Values are substituted in a "random" order; if any - substitution could be further expanded by another subsitition, it - is unpredictible whether the expansion will occur. -""" - -__revision__ = "src/engine/SCons/Tool/textfile.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons - -import os -import re - -from SCons.Node import Node -from SCons.Node.Python import Value -from SCons.Util import is_String, is_Sequence, is_Dict - -def _do_subst(node, subs): - """ - Fetch the node contents and replace all instances of the keys with - their values. For example, if subs is - {'%VERSION%': '1.2345', '%BASE%': 'MyProg', '%prefix%': '/bin'}, - then all instances of %VERSION% in the file will be replaced with - 1.2345 and so forth. - """ - contents = node.get_text_contents() - if not subs: return contents - for (k,v) in subs: - contents = re.sub(k, v, contents) - return contents - -def _action(target, source, env): - # prepare the line separator - linesep = env['LINESEPARATOR'] - if linesep is None: - linesep = os.linesep - elif is_String(linesep): - pass - elif isinstance(linesep, Value): - linesep = linesep.get_text_contents() - else: - raise SCons.Errors.UserError( - 'unexpected type/class for LINESEPARATOR: %s' - % repr(linesep), None) - - # create a dictionary to use for the substitutions - if 'SUBST_DICT' not in env: - subs = None # no substitutions - else: - d = env['SUBST_DICT'] - if is_Dict(d): - d = list(d.items()) - elif is_Sequence(d): - pass - else: - raise SCons.Errors.UserError('SUBST_DICT must be dict or sequence') - subs = [] - for (k,v) in d: - if callable(v): - v = v() - if is_String(v): - v = env.subst(v) - else: - v = str(v) - subs.append((k,v)) - - # write the file - try: - fd = open(target[0].get_path(), "wb") - except (OSError,IOError), e: - raise SCons.Errors.UserError("Can't write target file %s" % target[0]) - # separate lines by 'linesep' only if linesep is not empty - lsep = None - for s in source: - if lsep: fd.write(lsep) - fd.write(_do_subst(s, subs)) - lsep = linesep - fd.close() - -def _strfunc(target, source, env): - return "Creating '%s'" % target[0] - -def _convert_list_R(newlist, sources): - for elem in sources: - if is_Sequence(elem): - _convert_list_R(newlist, elem) - elif isinstance(elem, Node): - newlist.append(elem) - else: - newlist.append(Value(elem)) -def _convert_list(target, source, env): - if len(target) != 1: - raise SCons.Errors.UserError("Only one target file allowed") - newlist = [] - _convert_list_R(newlist, source) - return target, newlist - -_common_varlist = ['SUBST_DICT', 'LINESEPARATOR'] - -_text_varlist = _common_varlist + ['TEXTFILEPREFIX', 'TEXTFILESUFFIX'] -_text_builder = SCons.Builder.Builder( - action = SCons.Action.Action(_action, _strfunc, varlist = _text_varlist), - source_factory = Value, - emitter = _convert_list, - prefix = '$TEXTFILEPREFIX', - suffix = '$TEXTFILESUFFIX', - ) - -_subst_varlist = _common_varlist + ['SUBSTFILEPREFIX', 'TEXTFILESUFFIX'] -_subst_builder = SCons.Builder.Builder( - action = SCons.Action.Action(_action, _strfunc, varlist = _subst_varlist), - source_factory = SCons.Node.FS.File, - emitter = _convert_list, - prefix = '$SUBSTFILEPREFIX', - suffix = '$SUBSTFILESUFFIX', - src_suffix = ['.in'], - ) - -def generate(env): - env['LINESEPARATOR'] = os.linesep - env['BUILDERS']['Textfile'] = _text_builder - env['TEXTFILEPREFIX'] = '' - env['TEXTFILESUFFIX'] = '.txt' - env['BUILDERS']['Substfile'] = _subst_builder - env['SUBSTFILEPREFIX'] = '' - env['SUBSTFILESUFFIX'] = '' - -def exists(env): - return 1 - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/tlib.py b/scons/scons-local-2.2.0/SCons/Tool/tlib.py deleted file mode 100644 index 5a24a0cf2..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/tlib.py +++ /dev/null @@ -1,53 +0,0 @@ -"""SCons.Tool.tlib - -XXX - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/tlib.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Tool -import SCons.Tool.bcc32 -import SCons.Util - -def generate(env): - SCons.Tool.bcc32.findIt('tlib', env) - """Add Builders and construction variables for ar to an Environment.""" - SCons.Tool.createStaticLibBuilder(env) - env['AR'] = 'tlib' - env['ARFLAGS'] = SCons.Util.CLVar('') - env['ARCOM'] = '$AR $TARGET $ARFLAGS /a $SOURCES' - env['LIBPREFIX'] = '' - env['LIBSUFFIX'] = '.lib' - -def exists(env): - return SCons.Tool.bcc32.findIt('tlib', env) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/wix.py b/scons/scons-local-2.2.0/SCons/Tool/wix.py deleted file mode 100644 index eb88ce383..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/wix.py +++ /dev/null @@ -1,99 +0,0 @@ -"""SCons.Tool.wix - -Tool-specific initialization for wix, the Windows Installer XML Tool. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/wix.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import SCons.Builder -import SCons.Action -import os - -def generate(env): - """Add Builders and construction variables for WiX to an Environment.""" - if not exists(env): - return - - env['WIXCANDLEFLAGS'] = ['-nologo'] - env['WIXCANDLEINCLUDE'] = [] - env['WIXCANDLECOM'] = '$WIXCANDLE $WIXCANDLEFLAGS -I $WIXCANDLEINCLUDE -o ${TARGET} ${SOURCE}' - - env['WIXLIGHTFLAGS'].append( '-nologo' ) - env['WIXLIGHTCOM'] = "$WIXLIGHT $WIXLIGHTFLAGS -out ${TARGET} ${SOURCES}" - - object_builder = SCons.Builder.Builder( - action = '$WIXCANDLECOM', - suffix = '.wxiobj', - src_suffix = '.wxs') - - linker_builder = SCons.Builder.Builder( - action = '$WIXLIGHTCOM', - src_suffix = '.wxiobj', - src_builder = object_builder) - - env['BUILDERS']['WiX'] = linker_builder - -def exists(env): - env['WIXCANDLE'] = 'candle.exe' - env['WIXLIGHT'] = 'light.exe' - - # try to find the candle.exe and light.exe tools and - # add the install directory to light libpath. - #for path in os.environ['PATH'].split(os.pathsep): - for path in os.environ['PATH'].split(os.pathsep): - if not path: - continue - - # workaround for some weird python win32 bug. - if path[0] == '"' and path[-1:]=='"': - path = path[1:-1] - - # normalize the path - path = os.path.normpath(path) - - # search for the tools in the PATH environment variable - try: - if env['WIXCANDLE'] in os.listdir(path) and\ - env['WIXLIGHT'] in os.listdir(path): - env.PrependENVPath('PATH', path) - env['WIXLIGHTFLAGS'] = [ os.path.join( path, 'wixui.wixlib' ), - '-loc', - os.path.join( path, 'WixUI_en-us.wxl' ) ] - return 1 - except OSError: - pass # ignore this, could be a stale PATH entry. - - return None - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/xgettext.py b/scons/scons-local-2.2.0/SCons/Tool/xgettext.py deleted file mode 100644 index 9a5167c88..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/xgettext.py +++ /dev/null @@ -1,333 +0,0 @@ -""" xgettext tool - -Tool specific initialization of `xgettext` tool. -""" - -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Tool/xgettext.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -############################################################################# -class _CmdRunner(object): - """ Callabe object, which runs shell command storing its stdout and stderr to - variables. It also provides `strfunction()` method, which shall be used by - scons Action objects to print command string. """ - - def __init__( self, command, commandstr = None): - self.out = None - self.err = None - self.status = None - self.command = command - self.commandstr = commandstr - - def __call__(self, target, source, env): - import SCons.Action - import subprocess - import os - import sys - kw = { - 'stdin' : 'devnull', - 'stdout' : subprocess.PIPE, - 'stderr' : subprocess.PIPE, - 'universal_newlines' : True, - 'shell' : True - } - command = env.subst(self.command, target = target, source = source) - proc = SCons.Action._subproc(env, command, **kw) - self.out, self.err = proc.communicate() - self.status = proc.wait() - if self.err: sys.stderr.write(unicode(self.err)) - return self.status - - def strfunction(self, target, source, env): - import os - comstr = self.commandstr - if env.subst(comstr, target = target, source = source) == "": - comstr = self.command - s = env.subst(comstr, target = target, source = source) - return s -############################################################################# - -############################################################################# -def _update_pot_file(target, source, env): - """ Action function for `POTUpdate` builder """ - import re - import os - import SCons.Action - nop = lambda target, source, env : 0 - - # Save scons cwd and os cwd (NOTE: they may be different. After the job, we - # revert ech one to its original state). - save_cwd = env.fs.getcwd() - save_os_cwd = os.getcwd() - chdir = target[0].dir - chdir_str = repr(chdir.get_abspath()) - # Print chdir message (employ SCons.Action.Action for that. It knows better - # than me how to to this correctly). - env.Execute(SCons.Action.Action(nop, "Entering " + chdir_str)) - # Go to target's directory and do our job - env.fs.chdir(chdir, 1) # Go into target's directory - try: - cmd = _CmdRunner('$XGETTEXTCOM', '$XGETTEXTCOMSTR') - action = SCons.Action.Action(cmd, strfunction=cmd.strfunction) - status = action([ target[0] ], source, env) - except: - # Something went wrong. - env.Execute(SCons.Action.Action(nop, "Leaving " + chdir_str)) - # Revert working dirs to previous state and re-throw exception. - env.fs.chdir(save_cwd, 0) - os.chdir(save_os_cwd) - raise - # Print chdir message. - env.Execute(SCons.Action.Action(nop, "Leaving " + chdir_str)) - # Revert working dirs to previous state. - env.fs.chdir(save_cwd, 0) - os.chdir(save_os_cwd) - # If the command was not successfull, return error code. - if status: return status - - new_content = cmd.out - - if not new_content: - # When xgettext finds no internationalized messages, no *.pot is created - # (because we don't want to bother translators with empty POT files). - needs_update = False - explain = "no internationalized messages encountered" - else: - if target[0].exists(): - # If the file already exists, it's left unaltered unless its messages - # are outdated (w.r.t. to these recovered by xgettext from sources). - old_content = target[0].get_text_contents() - re_cdate = re.compile(r'^"POT-Creation-Date: .*"$[\r\n]?', re.M) - old_content_nocdate = re.sub(re_cdate,"",old_content) - new_content_nocdate = re.sub(re_cdate,"",new_content) - if(old_content_nocdate == new_content_nocdate): - # Messages are up-to-date - needs_update = False - explain = "messages in file found to be up-to-date" - else: - # Messages are outdated - needs_update = True - explain = "messages in file were outdated" - else: - # No POT file found, create new one - needs_update = True - explain = "new file" - if needs_update: - # Print message employing SCons.Action.Action for that. - msg = "Writting " + repr(str(target[0])) + " (" + explain + ")" - env.Execute(SCons.Action.Action(nop, msg)) - f = open(str(target[0]),"w") - f.write(new_content) - f.close() - return 0 - else: - # Print message employing SCons.Action.Action for that. - msg = "Not writting " + repr(str(target[0])) + " (" + explain + ")" - env.Execute(SCons.Action.Action(nop, msg)) - return 0 -############################################################################# - -############################################################################# -from SCons.Builder import BuilderBase -############################################################################# -class _POTBuilder(BuilderBase): - def _execute(self, env, target, source, *args): - if not target: - if env.has_key('POTDOMAIN') and env['POTDOMAIN']: - domain = env['POTDOMAIN'] - else: - domain = 'messages' - target = [ domain ] - return BuilderBase._execute(self, env, target, source, *args) -############################################################################# - -############################################################################# -def _scan_xgettext_from_files(target, source, env, files = None, path = None): - """ Parses `POTFILES.in`-like file and returns list of extracted file names. - """ - import re - import SCons.Util - import SCons.Node.FS - - if files is None: - return 0 - if not SCons.Util.is_List(files): - files = [ files ] - - if path is None: - if env.has_key('XGETTEXTPATH'): - path = env['XGETTEXTPATH'] - else: - path = [] - if not SCons.Util.is_List(path): - path = [ path ] - - path = SCons.Util.flatten(path) - - dirs = () - for p in path: - if not isinstance(p, SCons.Node.FS.Base): - if SCons.Util.is_String(p): - p = env.subst(p, source = source, target = target) - p = env.arg2nodes(p, env.fs.Dir) - dirs += tuple(p) - # cwd is the default search path (when no path is defined by user) - if not dirs: - dirs = (env.fs.getcwd(),) - - # Parse 'POTFILE.in' files. - re_comment = re.compile(r'^#[^\n\r]*$\r?\n?', re.M) - re_emptyln = re.compile(r'^[ \t\r]*$\r?\n?', re.M) - re_trailws = re.compile(r'[ \t\r]+$') - for f in files: - # Find files in search path $XGETTEXTPATH - if isinstance(f, SCons.Node.FS.Base) and f.rexists(): - contents = f.get_text_contents() - contents = re_comment.sub("", contents) - contents = re_emptyln.sub("", contents) - contents = re_trailws.sub("", contents) - depnames = contents.splitlines() - for depname in depnames: - depfile = SCons.Node.FS.find_file(depname, dirs) - if not depfile: - depfile = env.arg2nodes(depname, dirs[0].File) - env.Depends(target, depfile) - return 0 -############################################################################# - -############################################################################# -def _pot_update_emitter(target, source, env): - """ Emitter function for `POTUpdate` builder """ - from SCons.Tool.GettextCommon import _POTargetFactory - import SCons.Util - import SCons.Node.FS - - if env.has_key('XGETTEXTFROM'): - xfrom = env['XGETTEXTFROM'] - else: - return target, source - if not SCons.Util.is_List(xfrom): - xfrom = [ xfrom ] - - xfrom = SCons.Util.flatten(xfrom) - - # Prepare list of 'POTFILE.in' files. - files = [] - for xf in xfrom: - if not isinstance(xf, SCons.Node.FS.Base): - if SCons.Util.is_String(xf): - # Interpolate variables in strings - xf = env.subst(xf, source = source, target = target) - xf = env.arg2nodes(xf) - files.extend(xf) - if files: - env.Depends(target, files) - _scan_xgettext_from_files(target, source, env, files) - return target, source -############################################################################# - -############################################################################# -from SCons.Environment import _null -############################################################################# -def _POTUpdateBuilderWrapper(env, target=None, source=_null, **kw): - return env._POTUpdateBuilder(target, source, **kw) -############################################################################# - -############################################################################# -def _POTUpdateBuilder(env, **kw): - """ Creates `POTUpdate` builder object """ - import SCons.Action - from SCons.Tool.GettextCommon import _POTargetFactory - kw['action'] = SCons.Action.Action(_update_pot_file, None) - kw['suffix'] = '$POTSUFFIX' - kw['target_factory'] = _POTargetFactory(env, alias='$POTUPDATE_ALIAS').File - kw['emitter'] = _pot_update_emitter - return _POTBuilder(**kw) -############################################################################# - -############################################################################# -def generate(env,**kw): - """ Generate `xgettext` tool """ - import SCons.Util - from SCons.Tool.GettextCommon import RPaths, _detect_xgettext - - env['XGETTEXT'] = _detect_xgettext(env) - # NOTE: sources="$SOURCES" would work as well. However, we use following - # construction to convert absolute paths provided by scons onto paths - # relative to current working dir. Note, that scons expands $SOURCE(S) to - # absolute paths for sources $SOURCE(s) outside of current subtree (e.g. in - # "../"). With source=$SOURCE these absolute paths would be written to the - # resultant *.pot file (and its derived *.po files) as references to lines in - # source code (e.g. referring lines in *.c files). Such references would be - # correct (e.g. in poedit) only on machine on which *.pot was generated and - # would be of no use on other hosts (having a copy of source code located - # in different place in filesystem). - sources = '$( ${_concat( "", SOURCES, "", __env__, XgettextRPaths, TARGET' \ - + ', SOURCES)} $)' - - # NOTE: the output from $XGETTEXTCOM command must go to stdout, not to a file. - # This is required by the POTUpdate builder's action. - xgettextcom = '$XGETTEXT $XGETTEXTFLAGS $_XGETTEXTPATHFLAGS' \ - + ' $_XGETTEXTFROMFLAGS -o - ' + sources - - xgettextpathflags = '$( ${_concat( XGETTEXTPATHPREFIX, XGETTEXTPATH' \ - + ', XGETTEXTPATHSUFFIX, __env__, RDirs, TARGET, SOURCES)} $)' - xgettextfromflags = '$( ${_concat( XGETTEXTFROMPREFIX, XGETTEXTFROM' \ - + ', XGETTEXTFROMSUFFIX, __env__, target=TARGET, source=SOURCES)} $)' - - env.SetDefault( - _XGETTEXTDOMAIN = '${TARGET.filebase}', - XGETTEXTFLAGS = [ ], - XGETTEXTCOM = xgettextcom, - XGETTEXTCOMSTR = '', - XGETTEXTPATH = [ ], - XGETTEXTPATHPREFIX = '-D', - XGETTEXTPATHSUFFIX = '', - XGETTEXTFROM = None, - XGETTEXTFROMPREFIX = '-f', - XGETTEXTFROMSUFFIX = '', - _XGETTEXTPATHFLAGS = xgettextpathflags, - _XGETTEXTFROMFLAGS = xgettextfromflags, - POTSUFFIX = ['.pot'], - POTUPDATE_ALIAS = 'pot-update', - XgettextRPaths = RPaths(env) - ) - env.Append( BUILDERS = { - '_POTUpdateBuilder' : _POTUpdateBuilder(env) - } ) - env.AddMethod(_POTUpdateBuilderWrapper, 'POTUpdate') - env.AlwaysBuild(env.Alias('$POTUPDATE_ALIAS')) -############################################################################# - -############################################################################# -def exists(env): - """ Check, whether the tool exists """ - from SCons.Tool.GettextCommon import _xgettext_exists - return _xgettext_exists(env) -############################################################################# - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/yacc.py b/scons/scons-local-2.2.0/SCons/Tool/yacc.py deleted file mode 100644 index 580fe76ff..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/yacc.py +++ /dev/null @@ -1,140 +0,0 @@ -"""SCons.Tool.yacc - -Tool-specific initialization for yacc. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/yacc.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os.path - -import SCons.Defaults -import SCons.Tool -import SCons.Util - -YaccAction = SCons.Action.Action("$YACCCOM", "$YACCCOMSTR") - -def _yaccEmitter(target, source, env, ysuf, hsuf): - yaccflags = env.subst("$YACCFLAGS", target=target, source=source) - flags = SCons.Util.CLVar(yaccflags) - targetBase, targetExt = os.path.splitext(SCons.Util.to_String(target[0])) - - if '.ym' in ysuf: # If using Objective-C - target = [targetBase + ".m"] # the extension is ".m". - - - # If -d is specified on the command line, yacc will emit a .h - # or .hpp file with the same name as the .c or .cpp output file. - if '-d' in flags: - target.append(targetBase + env.subst(hsuf, target=target, source=source)) - - # If -g is specified on the command line, yacc will emit a .vcg - # file with the same base name as the .y, .yacc, .ym or .yy file. - if "-g" in flags: - base, ext = os.path.splitext(SCons.Util.to_String(source[0])) - target.append(base + env.subst("$YACCVCGFILESUFFIX")) - - # If -v is specirfied yacc will create the output debug file - # which is not really source for any process, but should - # be noted and also be cleaned - # Bug #2558 - if "-v" in flags: - env.SideEffect(targetBase+'.output',target[0]) - env.Clean(target[0],targetBase+'.output') - - - - # With --defines and --graph, the name of the file is totally defined - # in the options. - fileGenOptions = ["--defines=", "--graph="] - for option in flags: - for fileGenOption in fileGenOptions: - l = len(fileGenOption) - if option[:l] == fileGenOption: - # A file generating option is present, so add the file - # name to the list of targets. - fileName = option[l:].strip() - target.append(fileName) - - return (target, source) - -def yEmitter(target, source, env): - return _yaccEmitter(target, source, env, ['.y', '.yacc'], '$YACCHFILESUFFIX') - -def ymEmitter(target, source, env): - return _yaccEmitter(target, source, env, ['.ym'], '$YACCHFILESUFFIX') - -def yyEmitter(target, source, env): - return _yaccEmitter(target, source, env, ['.yy'], '$YACCHXXFILESUFFIX') - -def generate(env): - """Add Builders and construction variables for yacc to an Environment.""" - c_file, cxx_file = SCons.Tool.createCFileBuilders(env) - - # C - c_file.add_action('.y', YaccAction) - c_file.add_emitter('.y', yEmitter) - - c_file.add_action('.yacc', YaccAction) - c_file.add_emitter('.yacc', yEmitter) - - # Objective-C - c_file.add_action('.ym', YaccAction) - c_file.add_emitter('.ym', ymEmitter) - - # C++ - cxx_file.add_action('.yy', YaccAction) - cxx_file.add_emitter('.yy', yyEmitter) - - env['YACC'] = env.Detect('bison') or 'yacc' - env['YACCFLAGS'] = SCons.Util.CLVar('') - env['YACCCOM'] = '$YACC $YACCFLAGS -o $TARGET $SOURCES' - env['YACCHFILESUFFIX'] = '.h' - - # Apparently, OS X now creates file.hpp like everybody else - # I have no idea when it changed; it was fixed in 10.4 - #if env['PLATFORM'] == 'darwin': - # # Bison on Mac OS X just appends ".h" to the generated target .cc - # # or .cpp file name. Hooray for delayed expansion of variables. - # env['YACCHXXFILESUFFIX'] = '${TARGET.suffix}.h' - #else: - # env['YACCHXXFILESUFFIX'] = '.hpp' - env['YACCHXXFILESUFFIX'] = '.hpp' - - env['YACCVCGFILESUFFIX'] = '.vcg' - -def exists(env): - return env.Detect(['bison', 'yacc']) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Tool/zip.py b/scons/scons-local-2.2.0/SCons/Tool/zip.py deleted file mode 100644 index 77515ad35..000000000 --- a/scons/scons-local-2.2.0/SCons/Tool/zip.py +++ /dev/null @@ -1,99 +0,0 @@ -"""SCons.Tool.zip - -Tool-specific initialization for zip. - -There normally shouldn't be any need to import this module directly. -It will usually be imported through the generic SCons.Tool.Tool() -selection method. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Tool/zip.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os.path - -import SCons.Builder -import SCons.Defaults -import SCons.Node.FS -import SCons.Util - -try: - import zipfile - internal_zip = 1 -except ImportError: - internal_zip = 0 - -if internal_zip: - zipcompression = zipfile.ZIP_DEFLATED - def zip(target, source, env): - compression = env.get('ZIPCOMPRESSION', 0) - zf = zipfile.ZipFile(str(target[0]), 'w', compression) - for s in source: - if s.isdir(): - for dirpath, dirnames, filenames in os.walk(str(s)): - for fname in filenames: - path = os.path.join(dirpath, fname) - if os.path.isfile(path): - zf.write(path) - else: - zf.write(str(s)) - zf.close() -else: - zipcompression = 0 - zip = "$ZIP $ZIPFLAGS ${TARGET.abspath} $SOURCES" - - -zipAction = SCons.Action.Action(zip, varlist=['ZIPCOMPRESSION']) - -ZipBuilder = SCons.Builder.Builder(action = SCons.Action.Action('$ZIPCOM', '$ZIPCOMSTR'), - source_factory = SCons.Node.FS.Entry, - source_scanner = SCons.Defaults.DirScanner, - suffix = '$ZIPSUFFIX', - multi = 1) - - -def generate(env): - """Add Builders and construction variables for zip to an Environment.""" - try: - bld = env['BUILDERS']['Zip'] - except KeyError: - bld = ZipBuilder - env['BUILDERS']['Zip'] = bld - - env['ZIP'] = 'zip' - env['ZIPFLAGS'] = SCons.Util.CLVar('') - env['ZIPCOM'] = zipAction - env['ZIPCOMPRESSION'] = zipcompression - env['ZIPSUFFIX'] = '.zip' - -def exists(env): - return internal_zip or env.Detect('zip') - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Util.py b/scons/scons-local-2.2.0/SCons/Util.py deleted file mode 100644 index c1f87a2e7..000000000 --- a/scons/scons-local-2.2.0/SCons/Util.py +++ /dev/null @@ -1,1492 +0,0 @@ -"""SCons.Util - -Various utility functions go here. -""" -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Util.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os -import sys -import copy -import re -import types - -from collections import UserDict, UserList, UserString - -# Don't "from types import ..." these because we need to get at the -# types module later to look for UnicodeType. -InstanceType = types.InstanceType -MethodType = types.MethodType -FunctionType = types.FunctionType -try: unicode -except NameError: UnicodeType = None -else: UnicodeType = unicode - -def dictify(keys, values, result={}): - for k, v in zip(keys, values): - result[k] = v - return result - -_altsep = os.altsep -if _altsep is None and sys.platform == 'win32': - # My ActivePython 2.0.1 doesn't set os.altsep! What gives? - _altsep = '/' -if _altsep: - def rightmost_separator(path, sep): - return max(path.rfind(sep), path.rfind(_altsep)) -else: - def rightmost_separator(path, sep): - return path.rfind(sep) - -# First two from the Python Cookbook, just for completeness. -# (Yeah, yeah, YAGNI...) -def containsAny(str, set): - """Check whether sequence str contains ANY of the items in set.""" - for c in set: - if c in str: return 1 - return 0 - -def containsAll(str, set): - """Check whether sequence str contains ALL of the items in set.""" - for c in set: - if c not in str: return 0 - return 1 - -def containsOnly(str, set): - """Check whether sequence str contains ONLY items in set.""" - for c in str: - if c not in set: return 0 - return 1 - -def splitext(path): - "Same as os.path.splitext() but faster." - sep = rightmost_separator(path, os.sep) - dot = path.rfind('.') - # An ext is only real if it has at least one non-digit char - if dot > sep and not containsOnly(path[dot:], "0123456789."): - return path[:dot],path[dot:] - else: - return path,"" - -def updrive(path): - """ - Make the drive letter (if any) upper case. - This is useful because Windows is inconsitent on the case - of the drive letter, which can cause inconsistencies when - calculating command signatures. - """ - drive, rest = os.path.splitdrive(path) - if drive: - path = drive.upper() + rest - return path - -class NodeList(UserList): - """This class is almost exactly like a regular list of Nodes - (actually it can hold any object), with one important difference. - If you try to get an attribute from this list, it will return that - attribute from every item in the list. For example: - - >>> someList = NodeList([ ' foo ', ' bar ' ]) - >>> someList.strip() - [ 'foo', 'bar' ] - """ - def __nonzero__(self): - return len(self.data) != 0 - - def __str__(self): - return ' '.join(map(str, self.data)) - - def __iter__(self): - return iter(self.data) - - def __call__(self, *args, **kwargs): - result = [x(*args, **kwargs) for x in self.data] - return self.__class__(result) - - def __getattr__(self, name): - result = [getattr(x, name) for x in self.data] - return self.__class__(result) - - -_get_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[_a-zA-Z]\w*})$') - -def get_environment_var(varstr): - """Given a string, first determine if it looks like a reference - to a single environment variable, like "$FOO" or "${FOO}". - If so, return that variable with no decorations ("FOO"). - If not, return None.""" - mo=_get_env_var.match(to_String(varstr)) - if mo: - var = mo.group(1) - if var[0] == '{': - return var[1:-1] - else: - return var - else: - return None - -class DisplayEngine(object): - print_it = True - def __call__(self, text, append_newline=1): - if not self.print_it: - return - if append_newline: text = text + '\n' - try: - sys.stdout.write(unicode(text)) - except IOError: - # Stdout might be connected to a pipe that has been closed - # by now. The most likely reason for the pipe being closed - # is that the user has press ctrl-c. It this is the case, - # then SCons is currently shutdown. We therefore ignore - # IOError's here so that SCons can continue and shutdown - # properly so that the .sconsign is correctly written - # before SCons exits. - pass - - def set_mode(self, mode): - self.print_it = mode - -def render_tree(root, child_func, prune=0, margin=[0], visited={}): - """ - Render a tree of nodes into an ASCII tree view. - root - the root node of the tree - child_func - the function called to get the children of a node - prune - don't visit the same node twice - margin - the format of the left margin to use for children of root. - 1 results in a pipe, and 0 results in no pipe. - visited - a dictionary of visited nodes in the current branch if not prune, - or in the whole tree if prune. - """ - - rname = str(root) - - children = child_func(root) - retval = "" - for pipe in margin[:-1]: - if pipe: - retval = retval + "| " - else: - retval = retval + " " - - if rname in visited: - return retval + "+-[" + rname + "]\n" - - retval = retval + "+-" + rname + "\n" - if not prune: - visited = copy.copy(visited) - visited[rname] = 1 - - for i in range(len(children)): - margin.append(i 0 - last = t[0] - lasti = i = 1 - while i < n: - if t[i] != last: - t[lasti] = last = t[i] - lasti = lasti + 1 - i = i + 1 - return t[:lasti] - del t - - # Brute force is all that's left. - u = [] - for x in s: - if x not in u: - u.append(x) - return u - - - -# From Alex Martelli, -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560 -# ASPN: Python Cookbook: Remove duplicates from a sequence -# First comment, dated 2001/10/13. -# (Also in the printed Python Cookbook.) - -def uniquer(seq, idfun=None): - if idfun is None: - def idfun(x): return x - seen = {} - result = [] - for item in seq: - marker = idfun(item) - # in old Python versions: - # if seen.has_key(marker) - # but in new ones: - if marker in seen: continue - seen[marker] = 1 - result.append(item) - return result - -# A more efficient implementation of Alex's uniquer(), this avoids the -# idfun() argument and function-call overhead by assuming that all -# items in the sequence are hashable. - -def uniquer_hashables(seq): - seen = {} - result = [] - for item in seq: - #if not item in seen: - if item not in seen: - seen[item] = 1 - result.append(item) - return result - - - -# Much of the logic here was originally based on recipe 4.9 from the -# Python CookBook, but we had to dumb it way down for Python 1.5.2. -class LogicalLines(object): - - def __init__(self, fileobj): - self.fileobj = fileobj - - def readline(self): - result = [] - while True: - line = self.fileobj.readline() - if not line: - break - if line[-2:] == '\\\n': - result.append(line[:-2]) - else: - result.append(line) - break - return ''.join(result) - - def readlines(self): - result = [] - while True: - line = self.readline() - if not line: - break - result.append(line) - return result - - - -class UniqueList(UserList): - def __init__(self, seq = []): - UserList.__init__(self, seq) - self.unique = True - def __make_unique(self): - if not self.unique: - self.data = uniquer_hashables(self.data) - self.unique = True - def __lt__(self, other): - self.__make_unique() - return UserList.__lt__(self, other) - def __le__(self, other): - self.__make_unique() - return UserList.__le__(self, other) - def __eq__(self, other): - self.__make_unique() - return UserList.__eq__(self, other) - def __ne__(self, other): - self.__make_unique() - return UserList.__ne__(self, other) - def __gt__(self, other): - self.__make_unique() - return UserList.__gt__(self, other) - def __ge__(self, other): - self.__make_unique() - return UserList.__ge__(self, other) - def __cmp__(self, other): - self.__make_unique() - return UserList.__cmp__(self, other) - def __len__(self): - self.__make_unique() - return UserList.__len__(self) - def __getitem__(self, i): - self.__make_unique() - return UserList.__getitem__(self, i) - def __setitem__(self, i, item): - UserList.__setitem__(self, i, item) - self.unique = False - def __getslice__(self, i, j): - self.__make_unique() - return UserList.__getslice__(self, i, j) - def __setslice__(self, i, j, other): - UserList.__setslice__(self, i, j, other) - self.unique = False - def __add__(self, other): - result = UserList.__add__(self, other) - result.unique = False - return result - def __radd__(self, other): - result = UserList.__radd__(self, other) - result.unique = False - return result - def __iadd__(self, other): - result = UserList.__iadd__(self, other) - result.unique = False - return result - def __mul__(self, other): - result = UserList.__mul__(self, other) - result.unique = False - return result - def __rmul__(self, other): - result = UserList.__rmul__(self, other) - result.unique = False - return result - def __imul__(self, other): - result = UserList.__imul__(self, other) - result.unique = False - return result - def append(self, item): - UserList.append(self, item) - self.unique = False - def insert(self, i): - UserList.insert(self, i) - self.unique = False - def count(self, item): - self.__make_unique() - return UserList.count(self, item) - def index(self, item): - self.__make_unique() - return UserList.index(self, item) - def reverse(self): - self.__make_unique() - UserList.reverse(self) - def sort(self, *args, **kwds): - self.__make_unique() - return UserList.sort(self, *args, **kwds) - def extend(self, other): - UserList.extend(self, other) - self.unique = False - - -class Unbuffered(object): - """ - A proxy class that wraps a file object, flushing after every write, - and delegating everything else to the wrapped object. - """ - def __init__(self, file): - self.file = file - self.softspace = 0 ## backward compatibility; not supported in Py3k - def write(self, arg): - try: - self.file.write(arg) - self.file.flush() - except IOError: - # Stdout might be connected to a pipe that has been closed - # by now. The most likely reason for the pipe being closed - # is that the user has press ctrl-c. It this is the case, - # then SCons is currently shutdown. We therefore ignore - # IOError's here so that SCons can continue and shutdown - # properly so that the .sconsign is correctly written - # before SCons exits. - pass - def __getattr__(self, attr): - return getattr(self.file, attr) - -def make_path_relative(path): - """ makes an absolute path name to a relative pathname. - """ - if os.path.isabs(path): - drive_s,path = os.path.splitdrive(path) - - import re - if not drive_s: - path=re.compile("/*(.*)").findall(path)[0] - else: - path=path[1:] - - assert( not os.path.isabs( path ) ), path - return path - - - -# The original idea for AddMethod() and RenameFunction() come from the -# following post to the ActiveState Python Cookbook: -# -# ASPN: Python Cookbook : Install bound methods in an instance -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/223613 -# -# That code was a little fragile, though, so the following changes -# have been wrung on it: -# -# * Switched the installmethod() "object" and "function" arguments, -# so the order reflects that the left-hand side is the thing being -# "assigned to" and the right-hand side is the value being assigned. -# -# * Changed explicit type-checking to the "try: klass = object.__class__" -# block in installmethod() below so that it still works with the -# old-style classes that SCons uses. -# -# * Replaced the by-hand creation of methods and functions with use of -# the "new" module, as alluded to in Alex Martelli's response to the -# following Cookbook post: -# -# ASPN: Python Cookbook : Dynamically added methods to a class -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732 - -def AddMethod(obj, function, name=None): - """ - Adds either a bound method to an instance or an unbound method to - a class. If name is ommited the name of the specified function - is used by default. - Example: - a = A() - def f(self, x, y): - self.z = x + y - AddMethod(f, A, "add") - a.add(2, 4) - print a.z - AddMethod(lambda self, i: self.l[i], a, "listIndex") - print a.listIndex(5) - """ - if name is None: - name = function.func_name - else: - function = RenameFunction(function, name) - - if hasattr(obj, '__class__') and obj.__class__ is not type: - # "obj" is an instance, so it gets a bound method. - setattr(obj, name, MethodType(function, obj, obj.__class__)) - else: - # "obj" is a class, so it gets an unbound method. - setattr(obj, name, MethodType(function, None, obj)) - -def RenameFunction(function, name): - """ - Returns a function identical to the specified function, but with - the specified name. - """ - return FunctionType(function.func_code, - function.func_globals, - name, - function.func_defaults) - - -md5 = False -def MD5signature(s): - return str(s) - -def MD5filesignature(fname, chunksize=65536): - f = open(fname, "rb") - result = f.read() - f.close() - return result - -try: - import hashlib -except ImportError: - pass -else: - if hasattr(hashlib, 'md5'): - md5 = True - def MD5signature(s): - m = hashlib.md5() - m.update(str(s)) - return m.hexdigest() - - def MD5filesignature(fname, chunksize=65536): - m = hashlib.md5() - f = open(fname, "rb") - while True: - blck = f.read(chunksize) - if not blck: - break - m.update(str(blck)) - f.close() - return m.hexdigest() - -def MD5collect(signatures): - """ - Collects a list of signatures into an aggregate signature. - - signatures - a list of signatures - returns - the aggregate signature - """ - if len(signatures) == 1: - return signatures[0] - else: - return MD5signature(', '.join(signatures)) - - - -def silent_intern(x): - """ - Perform sys.intern() on the passed argument and return the result. - If the input is ineligible (e.g. a unicode string) the original argument is - returned and no exception is thrown. - """ - try: - return sys.intern(x) - except TypeError: - return x - - - -# From Dinu C. Gherman, -# Python Cookbook, second edition, recipe 6.17, p. 277. -# Also: -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/68205 -# ASPN: Python Cookbook: Null Object Design Pattern - -#TODO??? class Null(object): -class Null(object): - """ Null objects always and reliably "do nothing." """ - def __new__(cls, *args, **kwargs): - if not '_instance' in vars(cls): - cls._instance = super(Null, cls).__new__(cls, *args, **kwargs) - return cls._instance - def __init__(self, *args, **kwargs): - pass - def __call__(self, *args, **kwargs): - return self - def __repr__(self): - return "Null(0x%08X)" % id(self) - def __nonzero__(self): - return False - def __getattr__(self, name): - return self - def __setattr__(self, name, value): - return self - def __delattr__(self, name): - return self - -class NullSeq(Null): - def __len__(self): - return 0 - def __iter__(self): - return iter(()) - def __getitem__(self, i): - return self - def __delitem__(self, i): - return self - def __setitem__(self, i, v): - return self - - -del __revision__ - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Variables/BoolVariable.py b/scons/scons-local-2.2.0/SCons/Variables/BoolVariable.py deleted file mode 100644 index 492f95e32..000000000 --- a/scons/scons-local-2.2.0/SCons/Variables/BoolVariable.py +++ /dev/null @@ -1,89 +0,0 @@ -"""engine.SCons.Variables.BoolVariable - -This file defines the option type for SCons implementing true/false values. - -Usage example: - - opts = Variables() - opts.Add(BoolVariable('embedded', 'build for an embedded system', 0)) - ... - if env['embedded'] == 1: - ... -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Variables/BoolVariable.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -__all__ = ['BoolVariable',] - -import SCons.Errors - -__true_strings = ('y', 'yes', 'true', 't', '1', 'on' , 'all' ) -__false_strings = ('n', 'no', 'false', 'f', '0', 'off', 'none') - - -def _text2bool(val): - """ - Converts strings to True/False depending on the 'truth' expressed by - the string. If the string can't be converted, the original value - will be returned. - - See '__true_strings' and '__false_strings' for values considered - 'true' or 'false respectivly. - - This is usable as 'converter' for SCons' Variables. - """ - lval = val.lower() - if lval in __true_strings: return True - if lval in __false_strings: return False - raise ValueError("Invalid value for boolean option: %s" % val) - - -def _validator(key, val, env): - """ - Validates the given value to be either '0' or '1'. - - This is usable as 'validator' for SCons' Variables. - """ - if not env[key] in (True, False): - raise SCons.Errors.UserError( - 'Invalid value for boolean option %s: %s' % (key, env[key])) - - -def BoolVariable(key, help, default): - """ - The input parameters describe a boolen option, thus they are - returned with the correct converter and validator appended. The - 'help' text will by appended by '(yes|no) to show the valid - valued. The result is usable for input to opts.Add(). - """ - return (key, '%s (yes|no)' % help, default, - _validator, _text2bool) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Variables/EnumVariable.py b/scons/scons-local-2.2.0/SCons/Variables/EnumVariable.py deleted file mode 100644 index e12c133ce..000000000 --- a/scons/scons-local-2.2.0/SCons/Variables/EnumVariable.py +++ /dev/null @@ -1,103 +0,0 @@ -"""engine.SCons.Variables.EnumVariable - -This file defines the option type for SCons allowing only specified -input-values. - -Usage example: - - opts = Variables() - opts.Add(EnumVariable('debug', 'debug output and symbols', 'no', - allowed_values=('yes', 'no', 'full'), - map={}, ignorecase=2)) - ... - if env['debug'] == 'full': - ... -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Variables/EnumVariable.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -__all__ = ['EnumVariable',] - - -import SCons.Errors - -def _validator(key, val, env, vals): - if not val in vals: - raise SCons.Errors.UserError( - 'Invalid value for option %s: %s. Valid values are: %s' % (key, val, vals)) - - -def EnumVariable(key, help, default, allowed_values, map={}, ignorecase=0): - """ - The input parameters describe a option with only certain values - allowed. They are returned with an appropriate converter and - validator appended. The result is usable for input to - Variables.Add(). - - 'key' and 'default' are the values to be passed on to Variables.Add(). - - 'help' will be appended by the allowed values automatically - - 'allowed_values' is a list of strings, which are allowed as values - for this option. - - The 'map'-dictionary may be used for converting the input value - into canonical values (eg. for aliases). - - 'ignorecase' defines the behaviour of the validator: - - If ignorecase == 0, the validator/converter are case-sensitive. - If ignorecase == 1, the validator/converter are case-insensitive. - If ignorecase == 2, the validator/converter is case-insensitive and - the converted value will always be lower-case. - - The 'validator' tests whether the value is in the list of allowed - values. The 'converter' converts input values according to the - given 'map'-dictionary (unmapped input values are returned - unchanged). - """ - help = '%s (%s)' % (help, '|'.join(allowed_values)) - # define validator - if ignorecase >= 1: - validator = lambda key, val, env: \ - _validator(key, val.lower(), env, allowed_values) - else: - validator = lambda key, val, env: \ - _validator(key, val, env, allowed_values) - # define converter - if ignorecase == 2: - converter = lambda val: map.get(val.lower(), val).lower() - elif ignorecase == 1: - converter = lambda val: map.get(val.lower(), val) - else: - converter = lambda val: map.get(val, val) - return (key, help, default, validator, converter) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Variables/ListVariable.py b/scons/scons-local-2.2.0/SCons/Variables/ListVariable.py deleted file mode 100644 index 0763b29c4..000000000 --- a/scons/scons-local-2.2.0/SCons/Variables/ListVariable.py +++ /dev/null @@ -1,135 +0,0 @@ -"""engine.SCons.Variables.ListVariable - -This file defines the option type for SCons implementing 'lists'. - -A 'list' option may either be 'all', 'none' or a list of names -separated by comma. After the option has been processed, the option -value holds either the named list elements, all list elemens or no -list elements at all. - -Usage example: - - list_of_libs = Split('x11 gl qt ical') - - opts = Variables() - opts.Add(ListVariable('shared', - 'libraries to build as shared libraries', - 'all', - elems = list_of_libs)) - ... - for lib in list_of_libs: - if lib in env['shared']: - env.SharedObject(...) - else: - env.Object(...) -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Variables/ListVariable.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -# Know Bug: This should behave like a Set-Type, but does not really, -# since elements can occur twice. - -__all__ = ['ListVariable',] - -import collections - -import SCons.Util - - -class _ListVariable(collections.UserList): - def __init__(self, initlist=[], allowedElems=[]): - collections.UserList.__init__(self, [_f for _f in initlist if _f]) - self.allowedElems = sorted(allowedElems) - - def __cmp__(self, other): - raise NotImplementedError - def __eq__(self, other): - raise NotImplementedError - def __ge__(self, other): - raise NotImplementedError - def __gt__(self, other): - raise NotImplementedError - def __le__(self, other): - raise NotImplementedError - def __lt__(self, other): - raise NotImplementedError - def __str__(self): - if len(self) == 0: - return 'none' - self.data.sort() - if self.data == self.allowedElems: - return 'all' - else: - return ','.join(self) - def prepare_to_store(self): - return self.__str__() - -def _converter(val, allowedElems, mapdict): - """ - """ - if val == 'none': - val = [] - elif val == 'all': - val = allowedElems - else: - val = [_f for _f in val.split(',') if _f] - val = [mapdict.get(v, v) for v in val] - notAllowed = [v for v in val if not v in allowedElems] - if notAllowed: - raise ValueError("Invalid value(s) for option: %s" % - ','.join(notAllowed)) - return _ListVariable(val, allowedElems) - - -## def _validator(key, val, env): -## """ -## """ -## # todo: write validater for pgk list -## return 1 - - -def ListVariable(key, help, default, names, map={}): - """ - The input parameters describe a 'package list' option, thus they - are returned with the correct converter and validater appended. The - result is usable for input to opts.Add() . - - A 'package list' option may either be 'all', 'none' or a list of - package names (separated by space). - """ - names_str = 'allowed names: %s' % ' '.join(names) - if SCons.Util.is_List(default): - default = ','.join(default) - help = '\n '.join( - (help, '(all|none|comma-separated list of names)', names_str)) - return (key, help, default, - None, #_validator, - lambda val: _converter(val, names, map)) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Variables/PackageVariable.py b/scons/scons-local-2.2.0/SCons/Variables/PackageVariable.py deleted file mode 100644 index dfac082e4..000000000 --- a/scons/scons-local-2.2.0/SCons/Variables/PackageVariable.py +++ /dev/null @@ -1,106 +0,0 @@ -"""engine.SCons.Variables.PackageVariable - -This file defines the option type for SCons implementing 'package -activation'. - -To be used whenever a 'package' may be enabled/disabled and the -package path may be specified. - -Usage example: - - Examples: - x11=no (disables X11 support) - x11=yes (will search for the package installation dir) - x11=/usr/local/X11 (will check this path for existance) - - To replace autoconf's --with-xxx=yyy - - opts = Variables() - opts.Add(PackageVariable('x11', - 'use X11 installed here (yes = search some places', - 'yes')) - ... - if env['x11'] == True: - dir = ... search X11 in some standard places ... - env['x11'] = dir - if env['x11']: - ... build with x11 ... -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Variables/PackageVariable.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -__all__ = ['PackageVariable',] - -import SCons.Errors - -__enable_strings = ('1', 'yes', 'true', 'on', 'enable', 'search') -__disable_strings = ('0', 'no', 'false', 'off', 'disable') - -def _converter(val): - """ - """ - lval = val.lower() - if lval in __enable_strings: return True - if lval in __disable_strings: return False - #raise ValueError("Invalid value for boolean option: %s" % val) - return val - - -def _validator(key, val, env, searchfunc): - # NB: searchfunc is currenty undocumented and unsupported - """ - """ - # todo: write validator, check for path - import os - if env[key] is True: - if searchfunc: - env[key] = searchfunc(key, val) - elif env[key] and not os.path.exists(val): - raise SCons.Errors.UserError( - 'Path does not exist for option %s: %s' % (key, val)) - - -def PackageVariable(key, help, default, searchfunc=None): - # NB: searchfunc is currenty undocumented and unsupported - """ - The input parameters describe a 'package list' option, thus they - are returned with the correct converter and validator appended. The - result is usable for input to opts.Add() . - - A 'package list' option may either be 'all', 'none' or a list of - package names (seperated by space). - """ - help = '\n '.join( - (help, '( yes | no | /path/to/%s )' % key)) - return (key, help, default, - lambda k, v, e: _validator(k,v,e,searchfunc), - _converter) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Variables/PathVariable.py b/scons/scons-local-2.2.0/SCons/Variables/PathVariable.py deleted file mode 100644 index 77ef83eda..000000000 --- a/scons/scons-local-2.2.0/SCons/Variables/PathVariable.py +++ /dev/null @@ -1,147 +0,0 @@ -"""SCons.Variables.PathVariable - -This file defines an option type for SCons implementing path settings. - -To be used whenever a a user-specified path override should be allowed. - -Arguments to PathVariable are: - option-name = name of this option on the command line (e.g. "prefix") - option-help = help string for option - option-dflt = default value for this option - validator = [optional] validator for option value. Predefined - validators are: - - PathAccept -- accepts any path setting; no validation - PathIsDir -- path must be an existing directory - PathIsDirCreate -- path must be a dir; will create - PathIsFile -- path must be a file - PathExists -- path must exist (any type) [default] - - The validator is a function that is called and which - should return True or False to indicate if the path - is valid. The arguments to the validator function - are: (key, val, env). The key is the name of the - option, the val is the path specified for the option, - and the env is the env to which the Otions have been - added. - -Usage example: - - Examples: - prefix=/usr/local - - opts = Variables() - - opts = Variables() - opts.Add(PathVariable('qtdir', - 'where the root of Qt is installed', - qtdir, PathIsDir)) - opts.Add(PathVariable('qt_includes', - 'where the Qt includes are installed', - '$qtdir/includes', PathIsDirCreate)) - opts.Add(PathVariable('qt_libraries', - 'where the Qt library is installed', - '$qtdir/lib')) - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/Variables/PathVariable.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -__all__ = ['PathVariable',] - -import os -import os.path - -import SCons.Errors - -class _PathVariableClass(object): - - def PathAccept(self, key, val, env): - """Accepts any path, no checking done.""" - pass - - def PathIsDir(self, key, val, env): - """Validator to check if Path is a directory.""" - if not os.path.isdir(val): - if os.path.isfile(val): - m = 'Directory path for option %s is a file: %s' - else: - m = 'Directory path for option %s does not exist: %s' - raise SCons.Errors.UserError(m % (key, val)) - - def PathIsDirCreate(self, key, val, env): - """Validator to check if Path is a directory, - creating it if it does not exist.""" - if os.path.isfile(val): - m = 'Path for option %s is a file, not a directory: %s' - raise SCons.Errors.UserError(m % (key, val)) - if not os.path.isdir(val): - os.makedirs(val) - - def PathIsFile(self, key, val, env): - """validator to check if Path is a file""" - if not os.path.isfile(val): - if os.path.isdir(val): - m = 'File path for option %s is a directory: %s' - else: - m = 'File path for option %s does not exist: %s' - raise SCons.Errors.UserError(m % (key, val)) - - def PathExists(self, key, val, env): - """validator to check if Path exists""" - if not os.path.exists(val): - m = 'Path for option %s does not exist: %s' - raise SCons.Errors.UserError(m % (key, val)) - - def __call__(self, key, help, default, validator=None): - # NB: searchfunc is currenty undocumented and unsupported - """ - The input parameters describe a 'path list' option, thus they - are returned with the correct converter and validator appended. The - result is usable for input to opts.Add() . - - The 'default' option specifies the default path to use if the - user does not specify an override with this option. - - validator is a validator, see this file for examples - """ - if validator is None: - validator = self.PathExists - - if SCons.Util.is_List(key) or SCons.Util.is_Tuple(key): - return (key, '%s ( /path/to/%s )' % (help, key[0]), default, - validator, None) - else: - return (key, '%s ( /path/to/%s )' % (help, key), default, - validator, None) - -PathVariable = _PathVariableClass() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Variables/__init__.py b/scons/scons-local-2.2.0/SCons/Variables/__init__.py deleted file mode 100644 index 88df0218b..000000000 --- a/scons/scons-local-2.2.0/SCons/Variables/__init__.py +++ /dev/null @@ -1,312 +0,0 @@ -"""engine.SCons.Variables - -This file defines the Variables class that is used to add user-friendly -customizable variables to an SCons build. -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. - -__revision__ = "src/engine/SCons/Variables/__init__.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os.path -import sys - -import SCons.Environment -import SCons.Errors -import SCons.Util -import SCons.Warnings - -from BoolVariable import BoolVariable # okay -from EnumVariable import EnumVariable # okay -from ListVariable import ListVariable # naja -from PackageVariable import PackageVariable # naja -from PathVariable import PathVariable # okay - - -class Variables(object): - instance=None - - """ - Holds all the options, updates the environment with the variables, - and renders the help text. - """ - def __init__(self, files=[], args={}, is_global=1): - """ - files - [optional] List of option configuration files to load - (backward compatibility) If a single string is passed it is - automatically placed in a file list - """ - self.options = [] - self.args = args - if not SCons.Util.is_List(files): - if files: - files = [ files ] - else: - files = [] - self.files = files - self.unknown = {} - - # create the singleton instance - if is_global: - self=Variables.instance - - if not Variables.instance: - Variables.instance=self - - def _do_add(self, key, help="", default=None, validator=None, converter=None): - class Variable(object): - pass - - option = Variable() - - # if we get a list or a tuple, we take the first element as the - # option key and store the remaining in aliases. - if SCons.Util.is_List(key) or SCons.Util.is_Tuple(key): - option.key = key[0] - option.aliases = key[1:] - else: - option.key = key - option.aliases = [ key ] - option.help = help - option.default = default - option.validator = validator - option.converter = converter - - self.options.append(option) - - # options might be added after the 'unknown' dict has been set up, - # so we remove the key and all its aliases from that dict - for alias in list(option.aliases) + [ option.key ]: - if alias in self.unknown: - del self.unknown[alias] - - def keys(self): - """ - Returns the keywords for the options - """ - return [o.key for o in self.options] - - def Add(self, key, help="", default=None, validator=None, converter=None, **kw): - """ - Add an option. - - key - the name of the variable, or a list or tuple of arguments - help - optional help text for the options - default - optional default value - validator - optional function that is called to validate the option's value - Called with (key, value, environment) - converter - optional function that is called to convert the option's value before - putting it in the environment. - """ - - if SCons.Util.is_List(key) or isinstance(key, tuple): - self._do_add(*key) - return - - if not SCons.Util.is_String(key) or \ - not SCons.Environment.is_valid_construction_var(key): - raise SCons.Errors.UserError("Illegal Variables.Add() key `%s'" % str(key)) - - self._do_add(key, help, default, validator, converter) - - def AddVariables(self, *optlist): - """ - Add a list of options. - - Each list element is a tuple/list of arguments to be passed on - to the underlying method for adding options. - - Example: - opt.AddVariables( - ('debug', '', 0), - ('CC', 'The C compiler'), - ('VALIDATE', 'An option for testing validation', 'notset', - validator, None), - ) - """ - for o in optlist: - self._do_add(*o) - - - def Update(self, env, args=None): - """ - Update an environment with the option variables. - - env - the environment to update. - """ - - values = {} - - # first set the defaults: - for option in self.options: - if not option.default is None: - values[option.key] = option.default - - # next set the value specified in the options file - for filename in self.files: - if os.path.exists(filename): - dir = os.path.split(os.path.abspath(filename))[0] - if dir: - sys.path.insert(0, dir) - try: - values['__name__'] = filename - exec open(filename, 'rU').read() in {}, values - finally: - if dir: - del sys.path[0] - del values['__name__'] - - # set the values specified on the command line - if args is None: - args = self.args - - for arg, value in args.items(): - added = False - for option in self.options: - if arg in list(option.aliases) + [ option.key ]: - values[option.key] = value - added = True - if not added: - self.unknown[arg] = value - - # put the variables in the environment: - # (don't copy over variables that are not declared as options) - for option in self.options: - try: - env[option.key] = values[option.key] - except KeyError: - pass - - # Call the convert functions: - for option in self.options: - if option.converter and option.key in values: - value = env.subst('${%s}'%option.key) - try: - try: - env[option.key] = option.converter(value) - except TypeError: - env[option.key] = option.converter(value, env) - except ValueError, x: - raise SCons.Errors.UserError('Error converting option: %s\n%s'%(option.key, x)) - - - # Finally validate the values: - for option in self.options: - if option.validator and option.key in values: - option.validator(option.key, env.subst('${%s}'%option.key), env) - - def UnknownVariables(self): - """ - Returns any options in the specified arguments lists that - were not known, declared options in this object. - """ - return self.unknown - - def Save(self, filename, env): - """ - Saves all the options in the given file. This file can - then be used to load the options next run. This can be used - to create an option cache file. - - filename - Name of the file to save into - env - the environment get the option values from - """ - - # Create the file and write out the header - try: - fh = open(filename, 'w') - - try: - # Make an assignment in the file for each option - # within the environment that was assigned a value - # other than the default. - for option in self.options: - try: - value = env[option.key] - try: - prepare = value.prepare_to_store - except AttributeError: - try: - eval(repr(value)) - except KeyboardInterrupt: - raise - except: - # Convert stuff that has a repr() that - # cannot be evaluated into a string - value = SCons.Util.to_String(value) - else: - value = prepare() - - defaultVal = env.subst(SCons.Util.to_String(option.default)) - if option.converter: - defaultVal = option.converter(defaultVal) - - if str(env.subst('${%s}' % option.key)) != str(defaultVal): - fh.write('%s = %s\n' % (option.key, repr(value))) - except KeyError: - pass - finally: - fh.close() - - except IOError, x: - raise SCons.Errors.UserError('Error writing options to file: %s\n%s' % (filename, x)) - - def GenerateHelpText(self, env, sort=None): - """ - Generate the help text for the options. - - env - an environment that is used to get the current values - of the options. - """ - - if sort: - options = sorted(self.options, key=lambda x: x.key) - else: - options = self.options - - def format(opt, self=self, env=env): - if opt.key in env: - actual = env.subst('${%s}' % opt.key) - else: - actual = None - return self.FormatVariableHelpText(env, opt.key, opt.help, opt.default, actual, opt.aliases) - lines = [_f for _f in map(format, options) if _f] - - return ''.join(lines) - - format = '\n%s: %s\n default: %s\n actual: %s\n' - format_ = '\n%s: %s\n default: %s\n actual: %s\n aliases: %s\n' - - def FormatVariableHelpText(self, env, key, help, default, actual, aliases=[]): - # Don't display the key name itself as an alias. - aliases = [a for a in aliases if a != key] - if len(aliases)==0: - return self.format % (key, help, default, actual) - else: - return self.format_ % (key, help, default, actual, aliases) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/Warnings.py b/scons/scons-local-2.2.0/SCons/Warnings.py deleted file mode 100644 index 42e396f88..000000000 --- a/scons/scons-local-2.2.0/SCons/Warnings.py +++ /dev/null @@ -1,246 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -"""SCons.Warnings - -This file implements the warnings framework for SCons. - -""" - -__revision__ = "src/engine/SCons/Warnings.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import sys - -import SCons.Errors - -class Warning(SCons.Errors.UserError): - pass - -class WarningOnByDefault(Warning): - pass - - -# NOTE: If you add a new warning class, add it to the man page, too! - -class CacheWriteErrorWarning(Warning): - pass - -class CorruptSConsignWarning(WarningOnByDefault): - pass - -class DependencyWarning(Warning): - pass - -class DuplicateEnvironmentWarning(WarningOnByDefault): - pass - -class FutureReservedVariableWarning(WarningOnByDefault): - pass - -class LinkWarning(WarningOnByDefault): - pass - -class MisleadingKeywordsWarning(WarningOnByDefault): - pass - -class MissingSConscriptWarning(WarningOnByDefault): - pass - -class NoMD5ModuleWarning(WarningOnByDefault): - pass - -class NoMetaclassSupportWarning(WarningOnByDefault): - pass - -class NoObjectCountWarning(WarningOnByDefault): - pass - -class NoParallelSupportWarning(WarningOnByDefault): - pass - -class ReservedVariableWarning(WarningOnByDefault): - pass - -class StackSizeWarning(WarningOnByDefault): - pass - -class VisualCMissingWarning(WarningOnByDefault): - pass - -# Used when MSVC_VERSION and MSVS_VERSION do not point to the -# same version (MSVS_VERSION is deprecated) -class VisualVersionMismatch(WarningOnByDefault): - pass - -class VisualStudioMissingWarning(Warning): - pass - -class FortranCxxMixWarning(LinkWarning): - pass - - -# Deprecation warnings - -class FutureDeprecatedWarning(Warning): - pass - -class DeprecatedWarning(Warning): - pass - -class MandatoryDeprecatedWarning(DeprecatedWarning): - pass - - -# Special case; base always stays DeprecatedWarning -class PythonVersionWarning(DeprecatedWarning): - pass - -class DeprecatedSourceCodeWarning(FutureDeprecatedWarning): - pass - -class DeprecatedBuildDirWarning(DeprecatedWarning): - pass - -class TaskmasterNeedsExecuteWarning(DeprecatedWarning): - pass - -class DeprecatedCopyWarning(MandatoryDeprecatedWarning): - pass - -class DeprecatedOptionsWarning(MandatoryDeprecatedWarning): - pass - -class DeprecatedSourceSignaturesWarning(MandatoryDeprecatedWarning): - pass - -class DeprecatedTargetSignaturesWarning(MandatoryDeprecatedWarning): - pass - -class DeprecatedDebugOptionsWarning(MandatoryDeprecatedWarning): - pass - -class DeprecatedSigModuleWarning(MandatoryDeprecatedWarning): - pass - -class DeprecatedBuilderKeywordsWarning(MandatoryDeprecatedWarning): - pass - - -# The below is a list of 2-tuples. The first element is a class object. -# The second element is true if that class is enabled, false if it is disabled. -_enabled = [] - -# If set, raise the warning as an exception -_warningAsException = 0 - -# If not None, a function to call with the warning -_warningOut = None - -def suppressWarningClass(clazz): - """Suppresses all warnings that are of type clazz or - derived from clazz.""" - _enabled.insert(0, (clazz, 0)) - -def enableWarningClass(clazz): - """Enables all warnings that are of type clazz or - derived from clazz.""" - _enabled.insert(0, (clazz, 1)) - -def warningAsException(flag=1): - """Turn warnings into exceptions. Returns the old value of the flag.""" - global _warningAsException - old = _warningAsException - _warningAsException = flag - return old - -def warn(clazz, *args): - global _enabled, _warningAsException, _warningOut - - warning = clazz(args) - for clazz, flag in _enabled: - if isinstance(warning, clazz): - if flag: - if _warningAsException: - raise warning - - if _warningOut: - _warningOut(warning) - break - -def process_warn_strings(arguments): - """Process string specifications of enabling/disabling warnings, - as passed to the --warn option or the SetOption('warn') function. - - - An argument to this option should be of the form - or no-. The warning class is munged in order - to get an actual class name from the classes above, which we - need to pass to the {enable,disable}WarningClass() functions. - The supplied is split on hyphens, each element - is capitalized, then smushed back together. Then the string - "Warning" is appended to get the class name. - - For example, 'deprecated' will enable the DeprecatedWarning - class. 'no-dependency' will disable the DependencyWarning class. - - As a special case, --warn=all and --warn=no-all will enable or - disable (respectively) the base Warning class of all warnings. - - """ - - def _capitalize(s): - if s[:5] == "scons": - return "SCons" + s[5:] - else: - return s.capitalize() - - for arg in arguments: - - elems = arg.lower().split('-') - enable = 1 - if elems[0] == 'no': - enable = 0 - del elems[0] - - if len(elems) == 1 and elems[0] == 'all': - class_name = "Warning" - else: - class_name = ''.join(map(_capitalize, elems)) + "Warning" - try: - clazz = globals()[class_name] - except KeyError: - sys.stderr.write("No warning type: '%s'\n" % arg) - else: - if enable: - enableWarningClass(clazz) - elif issubclass(clazz, MandatoryDeprecatedWarning): - fmt = "Can not disable mandataory warning: '%s'\n" - sys.stderr.write(fmt % arg) - else: - suppressWarningClass(clazz) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/__init__.py b/scons/scons-local-2.2.0/SCons/__init__.py deleted file mode 100644 index d4b619f71..000000000 --- a/scons/scons-local-2.2.0/SCons/__init__.py +++ /dev/null @@ -1,49 +0,0 @@ -"""SCons - -The main package for the SCons software construction utility. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/__init__.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -__version__ = "2.2.0" - -__build__ = "issue-2856:2676:d23b7a2f45e8[MODIFIED]" - -__buildsys__ = "oberbrunner-dev" - -__date__ = "2012/08/05 15:38:28" - -__developer__ = "garyo" - -# make sure compatibility is always in place -import SCons.compat - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/compat/__init__.py b/scons/scons-local-2.2.0/SCons/compat/__init__.py deleted file mode 100644 index 7dc6a68d5..000000000 --- a/scons/scons-local-2.2.0/SCons/compat/__init__.py +++ /dev/null @@ -1,237 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__doc__ = """ -SCons compatibility package for old Python versions - -This subpackage holds modules that provide backwards-compatible -implementations of various things that we'd like to use in SCons but which -only show up in later versions of Python than the early, old version(s) -we still support. - -Other code will not generally reference things in this package through -the SCons.compat namespace. The modules included here add things to -the builtins namespace or the global module list so that the rest -of our code can use the objects and names imported here regardless of -Python version. - -Simply enough, things that go in the builtins name space come from -our _scons_builtins module. - -The rest of the things here will be in individual compatibility modules -that are either: 1) suitably modified copies of the future modules that -we want to use; or 2) backwards compatible re-implementations of the -specific portions of a future module's API that we want to use. - -GENERAL WARNINGS: Implementations of functions in the SCons.compat -modules are *NOT* guaranteed to be fully compliant with these functions in -later versions of Python. We are only concerned with adding functionality -that we actually use in SCons, so be wary if you lift this code for -other uses. (That said, making these more nearly the same as later, -official versions is still a desirable goal, we just don't need to be -obsessive about it.) - -We name the compatibility modules with an initial '_scons_' (for example, -_scons_subprocess.py is our compatibility module for subprocess) so -that we can still try to import the real module name and fall back to -our compatibility module if we get an ImportError. The import_as() -function defined below loads the module as the "real" name (without the -'_scons'), after which all of the "import {module}" statements in the -rest of our code will find our pre-loaded compatibility module. -""" - -__revision__ = "src/engine/SCons/compat/__init__.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import os -import sys -import imp # Use the "imp" module to protect imports from fixers. - -def import_as(module, name): - """ - Imports the specified module (from our local directory) as the - specified name, returning the loaded module object. - """ - dir = os.path.split(__file__)[0] - return imp.load_module(name, *imp.find_module(module, [dir])) - -def rename_module(new, old): - """ - Attempts to import the old module and load it under the new name. - Used for purely cosmetic name changes in Python 3.x. - """ - try: - sys.modules[new] = imp.load_module(old, *imp.find_module(old)) - return True - except ImportError: - return False - - -rename_module('builtins', '__builtin__') -import _scons_builtins - - -try: - import hashlib -except ImportError: - # Pre-2.5 Python has no hashlib module. - try: - import_as('_scons_hashlib', 'hashlib') - except ImportError: - # If we failed importing our compatibility module, it probably - # means this version of Python has no md5 module. Don't do - # anything and let the higher layer discover this fact, so it - # can fall back to using timestamp. - pass - -try: - set -except NameError: - # Pre-2.4 Python has no native set type - import_as('_scons_sets', 'sets') - import builtins, sets - builtins.set = sets.Set - - -try: - import collections -except ImportError: - # Pre-2.4 Python has no collections module. - import_as('_scons_collections', 'collections') -else: - try: - collections.UserDict - except AttributeError: - exec('from UserDict import UserDict as _UserDict') - collections.UserDict = _UserDict - del _UserDict - try: - collections.UserList - except AttributeError: - exec('from UserList import UserList as _UserList') - collections.UserList = _UserList - del _UserList - try: - collections.UserString - except AttributeError: - exec('from UserString import UserString as _UserString') - collections.UserString = _UserString - del _UserString - - -try: - import io -except ImportError: - # Pre-2.6 Python has no io module. - import_as('_scons_io', 'io') - - -try: - os.devnull -except AttributeError: - # Pre-2.4 Python has no os.devnull attribute - _names = sys.builtin_module_names - if 'posix' in _names: - os.devnull = '/dev/null' - elif 'nt' in _names: - os.devnull = 'nul' - os.path.devnull = os.devnull -try: - os.path.lexists -except AttributeError: - # Pre-2.4 Python has no os.path.lexists function - def lexists(path): - return os.path.exists(path) or os.path.islink(path) - os.path.lexists = lexists - - -# When we're using the '-3' option during regression tests, importing -# cPickle gives a warning no matter how it's done, so always use the -# real profile module, whether it's fast or not. -if os.environ.get('SCONS_HORRIBLE_REGRESSION_TEST_HACK') is None: - # Not a regression test with '-3', so try to use faster version. - # In 3.x, 'pickle' automatically loads the fast version if available. - rename_module('pickle', 'cPickle') - - -# In 3.x, 'profile' automatically loads the fast version if available. -rename_module('profile', 'cProfile') - - -# Before Python 3.0, the 'queue' module was named 'Queue'. -rename_module('queue', 'Queue') - - -# Before Python 3.0, the 'winreg' module was named '_winreg' -rename_module('winreg', '_winreg') - - -try: - import subprocess -except ImportError: - # Pre-2.4 Python has no subprocess module. - import_as('_scons_subprocess', 'subprocess') - -try: - sys.intern -except AttributeError: - # Pre-2.6 Python has no sys.intern() function. - import builtins - try: - sys.intern = builtins.intern - except AttributeError: - # Pre-2.x Python has no builtin intern() function. - def intern(x): - return x - sys.intern = intern - del intern -try: - sys.maxsize -except AttributeError: - # Pre-2.6 Python has no sys.maxsize attribute - # Wrapping sys in () is silly, but protects it from 2to3 renames fixer - sys.maxsize = (sys).maxint - - -if os.environ.get('SCONS_HORRIBLE_REGRESSION_TEST_HACK') is not None: - # We can't apply the 'callable' fixer until the floor is 2.6, but the - # '-3' option to Python 2.6 and 2.7 generates almost ten thousand - # warnings. This hack allows us to run regression tests with the '-3' - # option by replacing the callable() built-in function with a hack - # that performs the same function but doesn't generate the warning. - # Note that this hack is ONLY intended to be used for regression - # testing, and should NEVER be used for real runs. - from types import ClassType - def callable(obj): - if hasattr(obj, '__call__'): return True - if isinstance(obj, (ClassType, type)): return True - return False - import builtins - builtins.callable = callable - del callable - - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/compat/_scons_builtins.py b/scons/scons-local-2.2.0/SCons/compat/_scons_builtins.py deleted file mode 100644 index 32f4e7536..000000000 --- a/scons/scons-local-2.2.0/SCons/compat/_scons_builtins.py +++ /dev/null @@ -1,150 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -# Portions of the following are derived from the compat.py file in -# Twisted, under the following copyright: -# -# Copyright (c) 2001-2004 Twisted Matrix Laboratories - -__doc__ = """ -Compatibility idioms for builtins names - -This module adds names to the builtins module for things that we want -to use in SCons but which don't show up until later Python versions than -the earliest ones we support. - -This module checks for the following builtins names: - - all() - any() - sorted() - memoryview() - -Implementations of functions are *NOT* guaranteed to be fully compliant -with these functions in later versions of Python. We are only concerned -with adding functionality that we actually use in SCons, so be wary -if you lift this code for other uses. (That said, making these more -nearly the same as later, official versions is still a desirable goal, -we just don't need to be obsessive about it.) - -If you're looking at this with pydoc and various names don't show up in -the FUNCTIONS or DATA output, that means those names are already built in -to this version of Python and we don't need to add them from this module. -""" - -__revision__ = "src/engine/SCons/compat/_scons_builtins.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import builtins - -try: - all -except NameError: - # Pre-2.5 Python has no all() function. - def all(iterable): - """ - Returns True if all elements of the iterable are true. - """ - for element in iterable: - if not element: - return False - return True - builtins.all = all - all = all - -try: - any -except NameError: - # Pre-2.5 Python has no any() function. - def any(iterable): - """ - Returns True if any element of the iterable is true. - """ - for element in iterable: - if element: - return True - return False - builtins.any = any - any = any - -try: - memoryview -except NameError: - # Pre-2.7 doesn't have the memoryview() built-in. - class memoryview(object): - def __init__(self, obj): - # wrapping buffer in () keeps the fixer from changing it - self.obj = (buffer)(obj) - def __getitem__(self, indx): - if isinstance(indx, slice): - return self.obj[indx.start:indx.stop] - else: - return self.obj[indx] - builtins.memoryview = memoryview - -try: - sorted -except NameError: - # Pre-2.4 Python has no sorted() function. - # - # The pre-2.4 Python list.sort() method does not support - # list.sort(key=) nor list.sort(reverse=) keyword arguments, so - # we must implement the functionality of those keyword arguments - # by hand instead of passing them to list.sort(). - def sorted(iterable, cmp=None, key=None, reverse=False): - if key is not None: - result = [(key(x), x) for x in iterable] - else: - result = iterable[:] - if cmp is None: - # Pre-2.3 Python does not support list.sort(None). - result.sort() - else: - result.sort(cmp) - if key is not None: - result = [t1 for t0,t1 in result] - if reverse: - result.reverse() - return result - builtins.sorted = sorted - -#if sys.version_info[:3] in ((2, 2, 0), (2, 2, 1)): -# def lstrip(s, c=string.whitespace): -# while s and s[0] in c: -# s = s[1:] -# return s -# def rstrip(s, c=string.whitespace): -# while s and s[-1] in c: -# s = s[:-1] -# return s -# def strip(s, c=string.whitespace, l=lstrip, r=rstrip): -# return l(r(s, c), c) -# -# object.__setattr__(str, 'lstrip', lstrip) -# object.__setattr__(str, 'rstrip', rstrip) -# object.__setattr__(str, 'strip', strip) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/compat/_scons_collections.py b/scons/scons-local-2.2.0/SCons/compat/_scons_collections.py deleted file mode 100644 index 4687642dd..000000000 --- a/scons/scons-local-2.2.0/SCons/compat/_scons_collections.py +++ /dev/null @@ -1,45 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__doc__ = """ -collections compatibility module for older (pre-2.4) Python versions - -This does not not NOT (repeat, *NOT*) provide complete collections -functionality. It only wraps the portions of collections functionality -used by SCons, in an interface that looks enough like collections for -our purposes. -""" - -__revision__ = "src/engine/SCons/compat/_scons_collections.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -# Use exec to hide old names from fixers. -exec("""if True: - from UserDict import UserDict - from UserList import UserList - from UserString import UserString""") - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/compat/_scons_dbm.py b/scons/scons-local-2.2.0/SCons/compat/_scons_dbm.py deleted file mode 100644 index 0506ac804..000000000 --- a/scons/scons-local-2.2.0/SCons/compat/_scons_dbm.py +++ /dev/null @@ -1,45 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__doc__ = """ -dbm compatibility module for Python versions that don't have dbm. - -This does not not NOT (repeat, *NOT*) provide complete dbm functionality. -It's just a stub on which to hang just enough pieces of dbm functionality -that the whichdb.whichdb() implementstation in the various 2.X versions of -Python won't blow up even if dbm wasn't compiled in. -""" - -__revision__ = "src/engine/SCons/compat/_scons_dbm.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -class error(Exception): - pass - -def open(*args, **kw): - raise error() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/compat/_scons_hashlib.py b/scons/scons-local-2.2.0/SCons/compat/_scons_hashlib.py deleted file mode 100644 index 52cf3ba60..000000000 --- a/scons/scons-local-2.2.0/SCons/compat/_scons_hashlib.py +++ /dev/null @@ -1,76 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__doc__ = """ -hashlib backwards-compatibility module for older (pre-2.5) Python versions - -This does not not NOT (repeat, *NOT*) provide complete hashlib -functionality. It only wraps the portions of MD5 functionality used -by SCons, in an interface that looks like hashlib (or enough for our -purposes, anyway). In fact, this module will raise an ImportError if -the underlying md5 module isn't available. -""" - -__revision__ = "src/engine/SCons/compat/_scons_hashlib.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -import md5 -from string import hexdigits - -class md5obj(object): - - md5_module = md5 - - def __init__(self, name, string=''): - if not name in ('MD5', 'md5'): - raise ValueError("unsupported hash type") - self.name = 'md5' - self.m = self.md5_module.md5() - - def __repr__(self): - return '<%s HASH object @ %#x>' % (self.name, id(self)) - - def copy(self): - import copy - result = copy.copy(self) - result.m = self.m.copy() - return result - - def digest(self): - return self.m.digest() - - def update(self, arg): - return self.m.update(arg) - - def hexdigest(self): - return self.m.hexdigest() - -new = md5obj - -def md5(string=''): - return md5obj('md5', string) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/compat/_scons_io.py b/scons/scons-local-2.2.0/SCons/compat/_scons_io.py deleted file mode 100644 index df4d44497..000000000 --- a/scons/scons-local-2.2.0/SCons/compat/_scons_io.py +++ /dev/null @@ -1,45 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__doc__ = """ -io compatibility module for older (pre-2.6) Python versions - -This does not not NOT (repeat, *NOT*) provide complete io -functionality. It only wraps the portions of io functionality used -by SCons, in an interface that looks enough like io for our purposes. -""" - -__revision__ = "src/engine/SCons/compat/_scons_io.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -# Use the "imp" module to protect the imports below from fixers. -import imp - -_cStringIO = imp.load_module('cStringIO', *imp.find_module('cStringIO')) -StringIO = _cStringIO.StringIO -del _cStringIO - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/compat/_scons_sets.py b/scons/scons-local-2.2.0/SCons/compat/_scons_sets.py deleted file mode 100644 index 0fde9941d..000000000 --- a/scons/scons-local-2.2.0/SCons/compat/_scons_sets.py +++ /dev/null @@ -1,563 +0,0 @@ -"""Classes to represent arbitrary sets (including sets of sets). - -This module implements sets using dictionaries whose values are -ignored. The usual operations (union, intersection, deletion, etc.) -are provided as both methods and operators. - -Important: sets are not sequences! While they support 'x in s', -'len(s)', and 'for x in s', none of those operations are unique for -sequences; for example, mappings support all three as well. The -characteristic operation for sequences is subscripting with small -integers: s[i], for i in range(len(s)). Sets don't support -subscripting at all. Also, sequences allow multiple occurrences and -their elements have a definite order; sets on the other hand don't -record multiple occurrences and don't remember the order of element -insertion (which is why they don't support s[i]). - -The following classes are provided: - -BaseSet -- All the operations common to both mutable and immutable - sets. This is an abstract class, not meant to be directly - instantiated. - -Set -- Mutable sets, subclass of BaseSet; not hashable. - -ImmutableSet -- Immutable sets, subclass of BaseSet; hashable. - An iterable argument is mandatory to create an ImmutableSet. - -_TemporarilyImmutableSet -- A wrapper around a Set, hashable, - giving the same hash value as the immutable set equivalent - would have. Do not use this class directly. - -Only hashable objects can be added to a Set. In particular, you cannot -really add a Set as an element to another Set; if you try, what is -actually added is an ImmutableSet built from it (it compares equal to -the one you tried adding). - -When you ask if `x in y' where x is a Set and y is a Set or -ImmutableSet, x is wrapped into a _TemporarilyImmutableSet z, and -what's tested is actually `z in y'. - -""" - -# Code history: -# -# - Greg V. Wilson wrote the first version, using a different approach -# to the mutable/immutable problem, and inheriting from dict. -# -# - Alex Martelli modified Greg's version to implement the current -# Set/ImmutableSet approach, and make the data an attribute. -# -# - Guido van Rossum rewrote much of the code, made some API changes, -# and cleaned up the docstrings. -# -# - Raymond Hettinger added a number of speedups and other -# improvements. - -# protect this import from the fixers... -exec('from itertools import ifilterfalse as filterfalse') - -__all__ = ['BaseSet', 'Set', 'ImmutableSet'] - -class BaseSet(object): - """Common base class for mutable and immutable sets.""" - - __slots__ = ['_data'] - - # Constructor - - def __init__(self): - """This is an abstract class.""" - # Don't call this from a concrete subclass! - if self.__class__ is BaseSet: - raise TypeError("BaseSet is an abstract class. " - "Use Set or ImmutableSet.") - - # Standard protocols: __len__, __repr__, __str__, __iter__ - - def __len__(self): - """Return the number of elements of a set.""" - return len(self._data) - - def __repr__(self): - """Return string representation of a set. - - This looks like 'Set([])'. - """ - return self._repr() - - # __str__ is the same as __repr__ - __str__ = __repr__ - - def _repr(self, sort_them=False): - elements = list(self._data.keys()) - if sort_them: - elements.sort() - return '%s(%r)' % (self.__class__.__name__, elements) - - def __iter__(self): - """Return an iterator over the elements or a set. - - This is the keys iterator for the underlying dict. - """ - # Wrapping name in () prevents fixer from "fixing" this - return (self._data.iterkeys)() - - # Three-way comparison is not supported. However, because __eq__ is - # tried before __cmp__, if Set x == Set y, x.__eq__(y) returns True and - # then cmp(x, y) returns 0 (Python doesn't actually call __cmp__ in this - # case). - - def __cmp__(self, other): - raise TypeError("can't compare sets using cmp()") - - # Equality comparisons using the underlying dicts. Mixed-type comparisons - # are allowed here, where Set == z for non-Set z always returns False, - # and Set != z always True. This allows expressions like "x in y" to - # give the expected result when y is a sequence of mixed types, not - # raising a pointless TypeError just because y contains a Set, or x is - # a Set and y contain's a non-set ("in" invokes only __eq__). - # Subtle: it would be nicer if __eq__ and __ne__ could return - # NotImplemented instead of True or False. Then the other comparand - # would get a chance to determine the result, and if the other comparand - # also returned NotImplemented then it would fall back to object address - # comparison (which would always return False for __eq__ and always - # True for __ne__). However, that doesn't work, because this type - # *also* implements __cmp__: if, e.g., __eq__ returns NotImplemented, - # Python tries __cmp__ next, and the __cmp__ here then raises TypeError. - - def __eq__(self, other): - if isinstance(other, BaseSet): - return self._data == other._data - else: - return False - - def __ne__(self, other): - if isinstance(other, BaseSet): - return self._data != other._data - else: - return True - - # Copying operations - - def copy(self): - """Return a shallow copy of a set.""" - result = self.__class__() - result._data.update(self._data) - return result - - __copy__ = copy # For the copy module - - def __deepcopy__(self, memo): - """Return a deep copy of a set; used by copy module.""" - # This pre-creates the result and inserts it in the memo - # early, in case the deep copy recurses into another reference - # to this same set. A set can't be an element of itself, but - # it can certainly contain an object that has a reference to - # itself. - from copy import deepcopy - result = self.__class__() - memo[id(self)] = result - data = result._data - value = True - for elt in self: - data[deepcopy(elt, memo)] = value - return result - - # Standard set operations: union, intersection, both differences. - # Each has an operator version (e.g. __or__, invoked with |) and a - # method version (e.g. union). - # Subtle: Each pair requires distinct code so that the outcome is - # correct when the type of other isn't suitable. For example, if - # we did "union = __or__" instead, then Set().union(3) would return - # NotImplemented instead of raising TypeError (albeit that *why* it - # raises TypeError as-is is also a bit subtle). - - def __or__(self, other): - """Return the union of two sets as a new set. - - (I.e. all elements that are in either set.) - """ - if not isinstance(other, BaseSet): - return NotImplemented - return self.union(other) - - def union(self, other): - """Return the union of two sets as a new set. - - (I.e. all elements that are in either set.) - """ - result = self.__class__(self) - result._update(other) - return result - - def __and__(self, other): - """Return the intersection of two sets as a new set. - - (I.e. all elements that are in both sets.) - """ - if not isinstance(other, BaseSet): - return NotImplemented - return self.intersection(other) - - def intersection(self, other): - """Return the intersection of two sets as a new set. - - (I.e. all elements that are in both sets.) - """ - if not isinstance(other, BaseSet): - other = Set(other) - if len(self) <= len(other): - little, big = self, other - else: - little, big = other, self - common = iter(filter(big._data.has_key, little)) - return self.__class__(common) - - def __xor__(self, other): - """Return the symmetric difference of two sets as a new set. - - (I.e. all elements that are in exactly one of the sets.) - """ - if not isinstance(other, BaseSet): - return NotImplemented - return self.symmetric_difference(other) - - def symmetric_difference(self, other): - """Return the symmetric difference of two sets as a new set. - - (I.e. all elements that are in exactly one of the sets.) - """ - result = self.__class__() - data = result._data - value = True - selfdata = self._data - try: - otherdata = other._data - except AttributeError: - otherdata = Set(other)._data - for elt in filterfalse(otherdata.has_key, selfdata): - data[elt] = value - for elt in filterfalse(selfdata.has_key, otherdata): - data[elt] = value - return result - - def __sub__(self, other): - """Return the difference of two sets as a new Set. - - (I.e. all elements that are in this set and not in the other.) - """ - if not isinstance(other, BaseSet): - return NotImplemented - return self.difference(other) - - def difference(self, other): - """Return the difference of two sets as a new Set. - - (I.e. all elements that are in this set and not in the other.) - """ - result = self.__class__() - data = result._data - try: - otherdata = other._data - except AttributeError: - otherdata = Set(other)._data - value = True - for elt in filterfalse(otherdata.has_key, self): - data[elt] = value - return result - - # Membership test - - def __contains__(self, element): - """Report whether an element is a member of a set. - - (Called in response to the expression `element in self'.) - """ - try: - return element in self._data - except TypeError: - transform = getattr(element, "__as_temporarily_immutable__", None) - if transform is None: - raise # re-raise the TypeError exception we caught - return transform() in self._data - - # Subset and superset test - - def issubset(self, other): - """Report whether another set contains this set.""" - self._binary_sanity_check(other) - if len(self) > len(other): # Fast check for obvious cases - return False - for elt in filterfalse(other._data.has_key, self): - return False - return True - - def issuperset(self, other): - """Report whether this set contains another set.""" - self._binary_sanity_check(other) - if len(self) < len(other): # Fast check for obvious cases - return False - for elt in filterfalse(self._data.has_key, other): - return False - return True - - # Inequality comparisons using the is-subset relation. - __le__ = issubset - __ge__ = issuperset - - def __lt__(self, other): - self._binary_sanity_check(other) - return len(self) < len(other) and self.issubset(other) - - def __gt__(self, other): - self._binary_sanity_check(other) - return len(self) > len(other) and self.issuperset(other) - - # Assorted helpers - - def _binary_sanity_check(self, other): - # Check that the other argument to a binary operation is also - # a set, raising a TypeError otherwise. - if not isinstance(other, BaseSet): - raise TypeError("Binary operation only permitted between sets") - - def _compute_hash(self): - # Calculate hash code for a set by xor'ing the hash codes of - # the elements. This ensures that the hash code does not depend - # on the order in which elements are added to the set. This is - # not called __hash__ because a BaseSet should not be hashable; - # only an ImmutableSet is hashable. - result = 0 - for elt in self: - result ^= hash(elt) - return result - - def _update(self, iterable): - # The main loop for update() and the subclass __init__() methods. - data = self._data - - # Use the fast update() method when a dictionary is available. - if isinstance(iterable, BaseSet): - data.update(iterable._data) - return - - value = True - - if type(iterable) in (list, tuple, xrange): - # Optimized: we know that __iter__() and next() can't - # raise TypeError, so we can move 'try:' out of the loop. - it = iter(iterable) - while True: - try: - for element in it: - data[element] = value - return - except TypeError: - transform = getattr(element, "__as_immutable__", None) - if transform is None: - raise # re-raise the TypeError exception we caught - data[transform()] = value - else: - # Safe: only catch TypeError where intended - for element in iterable: - try: - data[element] = value - except TypeError: - transform = getattr(element, "__as_immutable__", None) - if transform is None: - raise # re-raise the TypeError exception we caught - data[transform()] = value - - -class ImmutableSet(BaseSet): - """Immutable set class.""" - - __slots__ = ['_hashcode'] - - # BaseSet + hashing - - def __init__(self, iterable=None): - """Construct an immutable set from an optional iterable.""" - self._hashcode = None - self._data = {} - if iterable is not None: - self._update(iterable) - - def __hash__(self): - if self._hashcode is None: - self._hashcode = self._compute_hash() - return self._hashcode - - def __getstate__(self): - return self._data, self._hashcode - - def __setstate__(self, state): - self._data, self._hashcode = state - -class Set(BaseSet): - """ Mutable set class.""" - - __slots__ = [] - - # BaseSet + operations requiring mutability; no hashing - - def __init__(self, iterable=None): - """Construct a set from an optional iterable.""" - self._data = {} - if iterable is not None: - self._update(iterable) - - def __getstate__(self): - # getstate's results are ignored if it is not - return self._data, - - def __setstate__(self, data): - self._data, = data - - def __hash__(self): - """A Set cannot be hashed.""" - # We inherit object.__hash__, so we must deny this explicitly - raise TypeError("Can't hash a Set, only an ImmutableSet.") - - # In-place union, intersection, differences. - # Subtle: The xyz_update() functions deliberately return None, - # as do all mutating operations on built-in container types. - # The __xyz__ spellings have to return self, though. - - def __ior__(self, other): - """Update a set with the union of itself and another.""" - self._binary_sanity_check(other) - self._data.update(other._data) - return self - - def union_update(self, other): - """Update a set with the union of itself and another.""" - self._update(other) - - def __iand__(self, other): - """Update a set with the intersection of itself and another.""" - self._binary_sanity_check(other) - self._data = (self & other)._data - return self - - def intersection_update(self, other): - """Update a set with the intersection of itself and another.""" - if isinstance(other, BaseSet): - self &= other - else: - self._data = (self.intersection(other))._data - - def __ixor__(self, other): - """Update a set with the symmetric difference of itself and another.""" - self._binary_sanity_check(other) - self.symmetric_difference_update(other) - return self - - def symmetric_difference_update(self, other): - """Update a set with the symmetric difference of itself and another.""" - data = self._data - value = True - if not isinstance(other, BaseSet): - other = Set(other) - if self is other: - self.clear() - for elt in other: - if elt in data: - del data[elt] - else: - data[elt] = value - - def __isub__(self, other): - """Remove all elements of another set from this set.""" - self._binary_sanity_check(other) - self.difference_update(other) - return self - - def difference_update(self, other): - """Remove all elements of another set from this set.""" - data = self._data - if not isinstance(other, BaseSet): - other = Set(other) - if self is other: - self.clear() - for elt in filter(data.has_key, other): - del data[elt] - - # Python dict-like mass mutations: update, clear - - def update(self, iterable): - """Add all values from an iterable (such as a list or file).""" - self._update(iterable) - - def clear(self): - """Remove all elements from this set.""" - self._data.clear() - - # Single-element mutations: add, remove, discard - - def add(self, element): - """Add an element to a set. - - This has no effect if the element is already present. - """ - try: - self._data[element] = True - except TypeError: - transform = getattr(element, "__as_immutable__", None) - if transform is None: - raise # re-raise the TypeError exception we caught - self._data[transform()] = True - - def remove(self, element): - """Remove an element from a set; it must be a member. - - If the element is not a member, raise a KeyError. - """ - try: - del self._data[element] - except TypeError: - transform = getattr(element, "__as_temporarily_immutable__", None) - if transform is None: - raise # re-raise the TypeError exception we caught - del self._data[transform()] - - def discard(self, element): - """Remove an element from a set if it is a member. - - If the element is not a member, do nothing. - """ - try: - self.remove(element) - except KeyError: - pass - - def pop(self): - """Remove and return an arbitrary set element.""" - return self._data.popitem()[0] - - def __as_immutable__(self): - # Return a copy of self as an immutable set - return ImmutableSet(self) - - def __as_temporarily_immutable__(self): - # Return self wrapped in a temporarily immutable set - return _TemporarilyImmutableSet(self) - - -class _TemporarilyImmutableSet(BaseSet): - # Wrap a mutable set as if it was temporarily immutable. - # This only supplies hashing and equality comparisons. - - def __init__(self, set): - self._set = set - self._data = set._data # Needed by ImmutableSet.__eq__() - - def __hash__(self): - return self._set._compute_hash() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/compat/_scons_subprocess.py b/scons/scons-local-2.2.0/SCons/compat/_scons_subprocess.py deleted file mode 100644 index eebe53d34..000000000 --- a/scons/scons-local-2.2.0/SCons/compat/_scons_subprocess.py +++ /dev/null @@ -1,1281 +0,0 @@ -# subprocess - Subprocesses with accessible I/O streams -# -# For more information about this module, see PEP 324. -# -# This module should remain compatible with Python 2.2, see PEP 291. -# -# Copyright (c) 2003-2005 by Peter Astrand -# -# Licensed to PSF under a Contributor Agreement. -# See http://www.python.org/2.4/license for licensing details. - -r"""subprocess - Subprocesses with accessible I/O streams - -This module allows you to spawn processes, connect to their -input/output/error pipes, and obtain their return codes. This module -intends to replace several other, older modules and functions, like: - -os.system -os.spawn* -os.popen* -popen2.* -commands.* - -Information about how the subprocess module can be used to replace these -modules and functions can be found below. - - - -Using the subprocess module -=========================== -This module defines one class called Popen: - -class Popen(args, bufsize=0, executable=None, - stdin=None, stdout=None, stderr=None, - preexec_fn=None, close_fds=False, shell=False, - cwd=None, env=None, universal_newlines=False, - startupinfo=None, creationflags=0): - - -Arguments are: - -args should be a string, or a sequence of program arguments. The -program to execute is normally the first item in the args sequence or -string, but can be explicitly set by using the executable argument. - -On UNIX, with shell=False (default): In this case, the Popen class -uses os.execvp() to execute the child program. args should normally -be a sequence. A string will be treated as a sequence with the string -as the only item (the program to execute). - -On UNIX, with shell=True: If args is a string, it specifies the -command string to execute through the shell. If args is a sequence, -the first item specifies the command string, and any additional items -will be treated as additional shell arguments. - -On Windows: the Popen class uses CreateProcess() to execute the child -program, which operates on strings. If args is a sequence, it will be -converted to a string using the list2cmdline method. Please note that -not all MS Windows applications interpret the command line the same -way: The list2cmdline is designed for applications using the same -rules as the MS C runtime. - -bufsize, if given, has the same meaning as the corresponding argument -to the built-in open() function: 0 means unbuffered, 1 means line -buffered, any other positive value means use a buffer of -(approximately) that size. A negative bufsize means to use the system -default, which usually means fully buffered. The default value for -bufsize is 0 (unbuffered). - -stdin, stdout and stderr specify the executed programs' standard -input, standard output and standard error file handles, respectively. -Valid values are PIPE, an existing file descriptor (a positive -integer), an existing file object, and None. PIPE indicates that a -new pipe to the child should be created. With None, no redirection -will occur; the child's file handles will be inherited from the -parent. Additionally, stderr can be STDOUT, which indicates that the -stderr data from the applications should be captured into the same -file handle as for stdout. - -If preexec_fn is set to a callable object, this object will be called -in the child process just before the child is executed. - -If close_fds is true, all file descriptors except 0, 1 and 2 will be -closed before the child process is executed. - -if shell is true, the specified command will be executed through the -shell. - -If cwd is not None, the current directory will be changed to cwd -before the child is executed. - -If env is not None, it defines the environment variables for the new -process. - -If universal_newlines is true, the file objects stdout and stderr are -opened as a text files, but lines may be terminated by any of '\n', -the Unix end-of-line convention, '\r', the Macintosh convention or -'\r\n', the Windows convention. All of these external representations -are seen as '\n' by the Python program. Note: This feature is only -available if Python is built with universal newline support (the -default). Also, the newlines attribute of the file objects stdout, -stdin and stderr are not updated by the communicate() method. - -The startupinfo and creationflags, if given, will be passed to the -underlying CreateProcess() function. They can specify things such as -appearance of the main window and priority for the new process. -(Windows only) - - -This module also defines two shortcut functions: - -call(*popenargs, **kwargs): - Run command with arguments. Wait for command to complete, then - return the returncode attribute. - - The arguments are the same as for the Popen constructor. Example: - - retcode = call(["ls", "-l"]) - -check_call(*popenargs, **kwargs): - Run command with arguments. Wait for command to complete. If the - exit code was zero then return, otherwise raise - CalledProcessError. The CalledProcessError object will have the - return code in the returncode attribute. - - The arguments are the same as for the Popen constructor. Example: - - check_call(["ls", "-l"]) - -Exceptions ----------- -Exceptions raised in the child process, before the new program has -started to execute, will be re-raised in the parent. Additionally, -the exception object will have one extra attribute called -'child_traceback', which is a string containing traceback information -from the childs point of view. - -The most common exception raised is OSError. This occurs, for -example, when trying to execute a non-existent file. Applications -should prepare for OSErrors. - -A ValueError will be raised if Popen is called with invalid arguments. - -check_call() will raise CalledProcessError, if the called process -returns a non-zero return code. - - -Security --------- -Unlike some other popen functions, this implementation will never call -/bin/sh implicitly. This means that all characters, including shell -metacharacters, can safely be passed to child processes. - - -Popen objects -============= -Instances of the Popen class have the following methods: - -poll() - Check if child process has terminated. Returns returncode - attribute. - -wait() - Wait for child process to terminate. Returns returncode attribute. - -communicate(input=None) - Interact with process: Send data to stdin. Read data from stdout - and stderr, until end-of-file is reached. Wait for process to - terminate. The optional stdin argument should be a string to be - sent to the child process, or None, if no data should be sent to - the child. - - communicate() returns a tuple (stdout, stderr). - - Note: The data read is buffered in memory, so do not use this - method if the data size is large or unlimited. - -The following attributes are also available: - -stdin - If the stdin argument is PIPE, this attribute is a file object - that provides input to the child process. Otherwise, it is None. - -stdout - If the stdout argument is PIPE, this attribute is a file object - that provides output from the child process. Otherwise, it is - None. - -stderr - If the stderr argument is PIPE, this attribute is file object that - provides error output from the child process. Otherwise, it is - None. - -pid - The process ID of the child process. - -returncode - The child return code. A None value indicates that the process - hasn't terminated yet. A negative value -N indicates that the - child was terminated by signal N (UNIX only). - - -Replacing older functions with the subprocess module -==================================================== -In this section, "a ==> b" means that b can be used as a replacement -for a. - -Note: All functions in this section fail (more or less) silently if -the executed program cannot be found; this module raises an OSError -exception. - -In the following examples, we assume that the subprocess module is -imported with "from subprocess import *". - - -Replacing /bin/sh shell backquote ---------------------------------- -output=`mycmd myarg` -==> -output = Popen(["mycmd", "myarg"], stdout=PIPE).communicate()[0] - - -Replacing shell pipe line -------------------------- -output=`dmesg | grep hda` -==> -p1 = Popen(["dmesg"], stdout=PIPE) -p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) -output = p2.communicate()[0] - - -Replacing os.system() ---------------------- -sts = os.system("mycmd" + " myarg") -==> -p = Popen("mycmd" + " myarg", shell=True) -pid, sts = os.waitpid(p.pid, 0) - -Note: - -* Calling the program through the shell is usually not required. - -* It's easier to look at the returncode attribute than the - exitstatus. - -A more real-world example would look like this: - -try: - retcode = call("mycmd" + " myarg", shell=True) - if retcode < 0: - print >>sys.stderr, "Child was terminated by signal", -retcode - else: - print >>sys.stderr, "Child returned", retcode -except OSError, e: - print >>sys.stderr, "Execution failed:", e - - -Replacing os.spawn* -------------------- -P_NOWAIT example: - -pid = os.spawnlp(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg") -==> -pid = Popen(["/bin/mycmd", "myarg"]).pid - - -P_WAIT example: - -retcode = os.spawnlp(os.P_WAIT, "/bin/mycmd", "mycmd", "myarg") -==> -retcode = call(["/bin/mycmd", "myarg"]) - - -Vector example: - -os.spawnvp(os.P_NOWAIT, path, args) -==> -Popen([path] + args[1:]) - - -Environment example: - -os.spawnlpe(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg", env) -==> -Popen(["/bin/mycmd", "myarg"], env={"PATH": "/usr/bin"}) - - -Replacing os.popen* -------------------- -pipe = os.popen(cmd, mode='r', bufsize) -==> -pipe = Popen(cmd, shell=True, bufsize=bufsize, stdout=PIPE).stdout - -pipe = os.popen(cmd, mode='w', bufsize) -==> -pipe = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE).stdin - - -(child_stdin, child_stdout) = os.popen2(cmd, mode, bufsize) -==> -p = Popen(cmd, shell=True, bufsize=bufsize, - stdin=PIPE, stdout=PIPE, close_fds=True) -(child_stdin, child_stdout) = (p.stdin, p.stdout) - - -(child_stdin, - child_stdout, - child_stderr) = os.popen3(cmd, mode, bufsize) -==> -p = Popen(cmd, shell=True, bufsize=bufsize, - stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) -(child_stdin, - child_stdout, - child_stderr) = (p.stdin, p.stdout, p.stderr) - - -(child_stdin, child_stdout_and_stderr) = os.popen4(cmd, mode, bufsize) -==> -p = Popen(cmd, shell=True, bufsize=bufsize, - stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) -(child_stdin, child_stdout_and_stderr) = (p.stdin, p.stdout) - - -Replacing popen2.* ------------------- -Note: If the cmd argument to popen2 functions is a string, the command -is executed through /bin/sh. If it is a list, the command is directly -executed. - -(child_stdout, child_stdin) = popen2.popen2("somestring", bufsize, mode) -==> -p = Popen(["somestring"], shell=True, bufsize=bufsize - stdin=PIPE, stdout=PIPE, close_fds=True) -(child_stdout, child_stdin) = (p.stdout, p.stdin) - - -(child_stdout, child_stdin) = popen2.popen2(["mycmd", "myarg"], bufsize, mode) -==> -p = Popen(["mycmd", "myarg"], bufsize=bufsize, - stdin=PIPE, stdout=PIPE, close_fds=True) -(child_stdout, child_stdin) = (p.stdout, p.stdin) - -The popen2.Popen3 and popen3.Popen4 basically works as subprocess.Popen, -except that: - -* subprocess.Popen raises an exception if the execution fails -* the capturestderr argument is replaced with the stderr argument. -* stdin=PIPE and stdout=PIPE must be specified. -* popen2 closes all filedescriptors by default, but you have to specify - close_fds=True with subprocess.Popen. - - -""" - -import sys -mswindows = (sys.platform == "win32") - -import os -import types -import traceback - -# Exception classes used by this module. -class CalledProcessError(Exception): - """This exception is raised when a process run by check_call() returns - a non-zero exit status. The exit status will be stored in the - returncode attribute.""" - def __init__(self, returncode, cmd): - self.returncode = returncode - self.cmd = cmd - def __str__(self): - return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode) - - -if mswindows: - try: - import threading - except ImportError: - # SCons: the threading module is only used by the communicate() - # method, which we don't actually use, so don't worry if we - # can't import it. - pass - import msvcrt - try: - # Try to get _subprocess - from _subprocess import * - class STARTUPINFO(object): - dwFlags = 0 - hStdInput = None - hStdOutput = None - hStdError = None - wShowWindow = 0 - class pywintypes(object): - error = IOError - except ImportError: - # If not there, then drop back to requiring pywin32 - # TODO: Should this be wrapped in try as well? To notify user to install - # pywin32 ? With URL to it? - import pywintypes - from win32api import GetStdHandle, STD_INPUT_HANDLE, \ - STD_OUTPUT_HANDLE, STD_ERROR_HANDLE - from win32api import GetCurrentProcess, DuplicateHandle, \ - GetModuleFileName, GetVersion - from win32con import DUPLICATE_SAME_ACCESS, SW_HIDE - from win32pipe import CreatePipe - from win32process import CreateProcess, STARTUPINFO, \ - GetExitCodeProcess, STARTF_USESTDHANDLES, \ - STARTF_USESHOWWINDOW, CREATE_NEW_CONSOLE - from win32event import WaitForSingleObject, INFINITE, WAIT_OBJECT_0 - - -else: - import select - import errno - import fcntl - import pickle - - try: - fcntl.F_GETFD - except AttributeError: - fcntl.F_GETFD = 1 - - try: - fcntl.F_SETFD - except AttributeError: - fcntl.F_SETFD = 2 - -__all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "CalledProcessError"] - -try: - MAXFD = os.sysconf("SC_OPEN_MAX") -except KeyboardInterrupt: - raise # SCons: don't swallow keyboard interrupts -except: - MAXFD = 256 - -try: - isinstance(1, int) -except TypeError: - def is_int(obj): - return isinstance(obj, type(1)) - def is_int_or_long(obj): - return type(obj) in (type(1), type(1L)) -else: - def is_int(obj): - return isinstance(obj, int) - def is_int_or_long(obj): - return isinstance(obj, (int, long)) - -try: - types.StringTypes -except AttributeError: - try: - types.StringTypes = (str, unicode) - except NameError: - types.StringTypes = (str,) -def is_string(obj): - return isinstance(obj, types.StringTypes) - -_active = [] - -def _cleanup(): - for inst in _active[:]: - if inst.poll(_deadstate=sys.maxsize) >= 0: - try: - _active.remove(inst) - except ValueError: - # This can happen if two threads create a new Popen instance. - # It's harmless that it was already removed, so ignore. - pass - -PIPE = -1 -STDOUT = -2 - - -def call(*popenargs, **kwargs): - """Run command with arguments. Wait for command to complete, then - return the returncode attribute. - - The arguments are the same as for the Popen constructor. Example: - - retcode = call(["ls", "-l"]) - """ - return apply(Popen, popenargs, kwargs).wait() - - -def check_call(*popenargs, **kwargs): - """Run command with arguments. Wait for command to complete. If - the exit code was zero then return, otherwise raise - CalledProcessError. The CalledProcessError object will have the - return code in the returncode attribute. - - The arguments are the same as for the Popen constructor. Example: - - check_call(["ls", "-l"]) - """ - retcode = call(*popenargs, **kwargs) - cmd = kwargs.get("args") - if cmd is None: - cmd = popenargs[0] - if retcode: - raise CalledProcessError(retcode, cmd) - return retcode - - -def list2cmdline(seq): - """ - Translate a sequence of arguments into a command line - string, using the same rules as the MS C runtime: - - 1) Arguments are delimited by white space, which is either a - space or a tab. - - 2) A string surrounded by double quotation marks is - interpreted as a single argument, regardless of white space - contained within. A quoted string can be embedded in an - argument. - - 3) A double quotation mark preceded by a backslash is - interpreted as a literal double quotation mark. - - 4) Backslashes are interpreted literally, unless they - immediately precede a double quotation mark. - - 5) If backslashes immediately precede a double quotation mark, - every pair of backslashes is interpreted as a literal - backslash. If the number of backslashes is odd, the last - backslash escapes the next double quotation mark as - described in rule 3. - """ - - # See - # http://msdn.microsoft.com/library/en-us/vccelng/htm/progs_12.asp - result = [] - needquote = False - for arg in seq: - bs_buf = [] - - # Add a space to separate this argument from the others - if result: - result.append(' ') - - needquote = (" " in arg) or ("\t" in arg) - if needquote: - result.append('"') - - for c in arg: - if c == '\\': - # Don't know if we need to double yet. - bs_buf.append(c) - elif c == '"': - # Double backspaces. - result.append('\\' * len(bs_buf)*2) - bs_buf = [] - result.append('\\"') - else: - # Normal char - if bs_buf: - result.extend(bs_buf) - bs_buf = [] - result.append(c) - - # Add remaining backspaces, if any. - if bs_buf: - result.extend(bs_buf) - - if needquote: - result.extend(bs_buf) - result.append('"') - - return ''.join(result) - -class Popen(object): - def __init__(self, args, bufsize=0, executable=None, - stdin=None, stdout=None, stderr=None, - preexec_fn=None, close_fds=False, shell=False, - cwd=None, env=None, universal_newlines=False, - startupinfo=None, creationflags=0): - """Create new Popen instance.""" - _cleanup() - - self._child_created = False - if not is_int_or_long(bufsize): - raise TypeError("bufsize must be an integer") - - if mswindows: - if preexec_fn is not None: - raise ValueError("preexec_fn is not supported on Windows " - "platforms") - if close_fds: - raise ValueError("close_fds is not supported on Windows " - "platforms") - else: - # POSIX - if startupinfo is not None: - raise ValueError("startupinfo is only supported on Windows " - "platforms") - if creationflags != 0: - raise ValueError("creationflags is only supported on Windows " - "platforms") - - self.stdin = None - self.stdout = None - self.stderr = None - self.pid = None - self.returncode = None - self.universal_newlines = universal_newlines - - # Input and output objects. The general principle is like - # this: - # - # Parent Child - # ------ ----- - # p2cwrite ---stdin---> p2cread - # c2pread <--stdout--- c2pwrite - # errread <--stderr--- errwrite - # - # On POSIX, the child objects are file descriptors. On - # Windows, these are Windows file handles. The parent objects - # are file descriptors on both platforms. The parent objects - # are None when not using PIPEs. The child objects are None - # when not redirecting. - - (p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite) = self._get_handles(stdin, stdout, stderr) - - self._execute_child(args, executable, preexec_fn, close_fds, - cwd, env, universal_newlines, - startupinfo, creationflags, shell, - p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite) - - if p2cwrite: - self.stdin = os.fdopen(p2cwrite, 'wb', bufsize) - if c2pread: - if universal_newlines: - self.stdout = os.fdopen(c2pread, 'rU', bufsize) - else: - self.stdout = os.fdopen(c2pread, 'rb', bufsize) - if errread: - if universal_newlines: - self.stderr = os.fdopen(errread, 'rU', bufsize) - else: - self.stderr = os.fdopen(errread, 'rb', bufsize) - - - def _translate_newlines(self, data): - data = data.replace("\r\n", "\n") - data = data.replace("\r", "\n") - return data - - - def __del__(self): - if not self._child_created: - # We didn't get to successfully create a child process. - return - # In case the child hasn't been waited on, check if it's done. - self.poll(_deadstate=sys.maxsize) - if self.returncode is None and _active is not None: - # Child is still running, keep us alive until we can wait on it. - _active.append(self) - - - def communicate(self, input=None): - """Interact with process: Send data to stdin. Read data from - stdout and stderr, until end-of-file is reached. Wait for - process to terminate. The optional input argument should be a - string to be sent to the child process, or None, if no data - should be sent to the child. - - communicate() returns a tuple (stdout, stderr).""" - - # Optimization: If we are only using one pipe, or no pipe at - # all, using select() or threads is unnecessary. - if [self.stdin, self.stdout, self.stderr].count(None) >= 2: - stdout = None - stderr = None - if self.stdin: - if input: - self.stdin.write(input) - self.stdin.close() - elif self.stdout: - stdout = self.stdout.read() - elif self.stderr: - stderr = self.stderr.read() - self.wait() - return (stdout, stderr) - - return self._communicate(input) - - - if mswindows: - # - # Windows methods - # - def _get_handles(self, stdin, stdout, stderr): - """Construct and return tupel with IO objects: - p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite - """ - if stdin is None and stdout is None and stderr is None: - return (None, None, None, None, None, None) - - p2cread, p2cwrite = None, None - c2pread, c2pwrite = None, None - errread, errwrite = None, None - - if stdin is None: - p2cread = GetStdHandle(STD_INPUT_HANDLE) - elif stdin == PIPE: - p2cread, p2cwrite = CreatePipe(None, 0) - # Detach and turn into fd - p2cwrite = p2cwrite.Detach() - p2cwrite = msvcrt.open_osfhandle(p2cwrite, 0) - elif is_int(stdin): - p2cread = msvcrt.get_osfhandle(stdin) - else: - # Assuming file-like object - p2cread = msvcrt.get_osfhandle(stdin.fileno()) - p2cread = self._make_inheritable(p2cread) - - if stdout is None: - c2pwrite = GetStdHandle(STD_OUTPUT_HANDLE) - elif stdout == PIPE: - c2pread, c2pwrite = CreatePipe(None, 0) - # Detach and turn into fd - c2pread = c2pread.Detach() - c2pread = msvcrt.open_osfhandle(c2pread, 0) - elif is_int(stdout): - c2pwrite = msvcrt.get_osfhandle(stdout) - else: - # Assuming file-like object - c2pwrite = msvcrt.get_osfhandle(stdout.fileno()) - c2pwrite = self._make_inheritable(c2pwrite) - - if stderr is None: - errwrite = GetStdHandle(STD_ERROR_HANDLE) - elif stderr == PIPE: - errread, errwrite = CreatePipe(None, 0) - # Detach and turn into fd - errread = errread.Detach() - errread = msvcrt.open_osfhandle(errread, 0) - elif stderr == STDOUT: - errwrite = c2pwrite - elif is_int(stderr): - errwrite = msvcrt.get_osfhandle(stderr) - else: - # Assuming file-like object - errwrite = msvcrt.get_osfhandle(stderr.fileno()) - errwrite = self._make_inheritable(errwrite) - - return (p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite) - - - def _make_inheritable(self, handle): - """Return a duplicate of handle, which is inheritable""" - return DuplicateHandle(GetCurrentProcess(), handle, - GetCurrentProcess(), 0, 1, - DUPLICATE_SAME_ACCESS) - - - def _find_w9xpopen(self): - """Find and return absolut path to w9xpopen.exe""" - w9xpopen = os.path.join(os.path.dirname(GetModuleFileName(0)), - "w9xpopen.exe") - if not os.path.exists(w9xpopen): - # Eeek - file-not-found - possibly an embedding - # situation - see if we can locate it in sys.exec_prefix - w9xpopen = os.path.join(os.path.dirname(sys.exec_prefix), - "w9xpopen.exe") - if not os.path.exists(w9xpopen): - raise RuntimeError("Cannot locate w9xpopen.exe, which is " - "needed for Popen to work with your " - "shell or platform.") - return w9xpopen - - - def _execute_child(self, args, executable, preexec_fn, close_fds, - cwd, env, universal_newlines, - startupinfo, creationflags, shell, - p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite): - """Execute program (MS Windows version)""" - - if not isinstance(args, types.StringTypes): - args = list2cmdline(args) - - # Process startup details - if startupinfo is None: - startupinfo = STARTUPINFO() - if None not in (p2cread, c2pwrite, errwrite): - startupinfo.dwFlags = startupinfo.dwFlags | STARTF_USESTDHANDLES - startupinfo.hStdInput = p2cread - startupinfo.hStdOutput = c2pwrite - startupinfo.hStdError = errwrite - - if shell: - startupinfo.dwFlags = startupinfo.dwFlags | STARTF_USESHOWWINDOW - startupinfo.wShowWindow = SW_HIDE - comspec = os.environ.get("COMSPEC", "cmd.exe") - args = comspec + " /c " + args - if (GetVersion() >= 0x80000000L or - os.path.basename(comspec).lower() == "command.com"): - # Win9x, or using command.com on NT. We need to - # use the w9xpopen intermediate program. For more - # information, see KB Q150956 - # (http://web.archive.org/web/20011105084002/http://support.microsoft.com/support/kb/articles/Q150/9/56.asp) - w9xpopen = self._find_w9xpopen() - args = '"%s" %s' % (w9xpopen, args) - # Not passing CREATE_NEW_CONSOLE has been known to - # cause random failures on win9x. Specifically a - # dialog: "Your program accessed mem currently in - # use at xxx" and a hopeful warning about the - # stability of your system. Cost is Ctrl+C wont - # kill children. - creationflags = creationflags | CREATE_NEW_CONSOLE - - # Start the process - try: - hp, ht, pid, tid = CreateProcess(executable, args, - # no special security - None, None, - # must inherit handles to pass std - # handles - 1, - creationflags, - env, - cwd, - startupinfo) - except pywintypes.error, e: - # Translate pywintypes.error to WindowsError, which is - # a subclass of OSError. FIXME: We should really - # translate errno using _sys_errlist (or simliar), but - # how can this be done from Python? - raise WindowsError(*e.args) - - # Retain the process handle, but close the thread handle - self._child_created = True - self._handle = hp - self.pid = pid - ht.Close() - - # Child is launched. Close the parent's copy of those pipe - # handles that only the child should have open. You need - # to make sure that no handles to the write end of the - # output pipe are maintained in this process or else the - # pipe will not close when the child process exits and the - # ReadFile will hang. - if p2cread is not None: - p2cread.Close() - if c2pwrite is not None: - c2pwrite.Close() - if errwrite is not None: - errwrite.Close() - - - def poll(self, _deadstate=None): - """Check if child process has terminated. Returns returncode - attribute.""" - if self.returncode is None: - if WaitForSingleObject(self._handle, 0) == WAIT_OBJECT_0: - self.returncode = GetExitCodeProcess(self._handle) - return self.returncode - - - def wait(self): - """Wait for child process to terminate. Returns returncode - attribute.""" - if self.returncode is None: - obj = WaitForSingleObject(self._handle, INFINITE) - self.returncode = GetExitCodeProcess(self._handle) - return self.returncode - - - def _readerthread(self, fh, buffer): - buffer.append(fh.read()) - - - def _communicate(self, input): - stdout = None # Return - stderr = None # Return - - if self.stdout: - stdout = [] - stdout_thread = threading.Thread(target=self._readerthread, - args=(self.stdout, stdout)) - stdout_thread.setDaemon(True) - stdout_thread.start() - if self.stderr: - stderr = [] - stderr_thread = threading.Thread(target=self._readerthread, - args=(self.stderr, stderr)) - stderr_thread.setDaemon(True) - stderr_thread.start() - - if self.stdin: - if input is not None: - self.stdin.write(input) - self.stdin.close() - - if self.stdout: - stdout_thread.join() - if self.stderr: - stderr_thread.join() - - # All data exchanged. Translate lists into strings. - if stdout is not None: - stdout = stdout[0] - if stderr is not None: - stderr = stderr[0] - - # Translate newlines, if requested. We cannot let the file - # object do the translation: It is based on stdio, which is - # impossible to combine with select (unless forcing no - # buffering). - if self.universal_newlines and hasattr(file, 'newlines'): - if stdout: - stdout = self._translate_newlines(stdout) - if stderr: - stderr = self._translate_newlines(stderr) - - self.wait() - return (stdout, stderr) - - else: - # - # POSIX methods - # - def _get_handles(self, stdin, stdout, stderr): - """Construct and return tupel with IO objects: - p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite - """ - p2cread, p2cwrite = None, None - c2pread, c2pwrite = None, None - errread, errwrite = None, None - - if stdin is None: - pass - elif stdin == PIPE: - p2cread, p2cwrite = os.pipe() - elif is_int(stdin): - p2cread = stdin - else: - # Assuming file-like object - p2cread = stdin.fileno() - - if stdout is None: - pass - elif stdout == PIPE: - c2pread, c2pwrite = os.pipe() - elif is_int(stdout): - c2pwrite = stdout - else: - # Assuming file-like object - c2pwrite = stdout.fileno() - - if stderr is None: - pass - elif stderr == PIPE: - errread, errwrite = os.pipe() - elif stderr == STDOUT: - errwrite = c2pwrite - elif is_int(stderr): - errwrite = stderr - else: - # Assuming file-like object - errwrite = stderr.fileno() - - return (p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite) - - - def _set_cloexec_flag(self, fd): - try: - cloexec_flag = fcntl.FD_CLOEXEC - except AttributeError: - cloexec_flag = 1 - - old = fcntl.fcntl(fd, fcntl.F_GETFD) - fcntl.fcntl(fd, fcntl.F_SETFD, old | cloexec_flag) - - - def _close_fds(self, but): - for i in range(3, MAXFD): - if i == but: - continue - try: - os.close(i) - except KeyboardInterrupt: - raise # SCons: don't swallow keyboard interrupts - except: - pass - - - def _execute_child(self, args, executable, preexec_fn, close_fds, - cwd, env, universal_newlines, - startupinfo, creationflags, shell, - p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite): - """Execute program (POSIX version)""" - - if is_string(args): - args = [args] - - if shell: - args = ["/bin/sh", "-c"] + args - - if executable is None: - executable = args[0] - - # For transferring possible exec failure from child to parent - # The first char specifies the exception type: 0 means - # OSError, 1 means some other error. - errpipe_read, errpipe_write = os.pipe() - self._set_cloexec_flag(errpipe_write) - - self.pid = os.fork() - self._child_created = True - if self.pid == 0: - # Child - try: - # Close parent's pipe ends - if p2cwrite: - os.close(p2cwrite) - if c2pread: - os.close(c2pread) - if errread: - os.close(errread) - os.close(errpipe_read) - - # Dup fds for child - if p2cread: - os.dup2(p2cread, 0) - if c2pwrite: - os.dup2(c2pwrite, 1) - if errwrite: - os.dup2(errwrite, 2) - - # Close pipe fds. Make sure we don't close the same - # fd more than once, or standard fds. - try: - set - except NameError: - # Fall-back for earlier Python versions, so epydoc - # can use this module directly to execute things. - if p2cread: - os.close(p2cread) - if c2pwrite and c2pwrite not in (p2cread,): - os.close(c2pwrite) - if errwrite and errwrite not in (p2cread, c2pwrite): - os.close(errwrite) - else: - for fd in set((p2cread, c2pwrite, errwrite))-set((0,1,2)): - if fd: os.close(fd) - - # Close all other fds, if asked for - if close_fds: - self._close_fds(but=errpipe_write) - - if cwd is not None: - os.chdir(cwd) - - if preexec_fn: - apply(preexec_fn) - - if env is None: - os.execvp(executable, args) - else: - os.execvpe(executable, args, env) - - except KeyboardInterrupt: - raise # SCons: don't swallow keyboard interrupts - - except: - exc_type, exc_value, tb = sys.exc_info() - # Save the traceback and attach it to the exception object - exc_lines = traceback.format_exception(exc_type, - exc_value, - tb) - exc_value.child_traceback = ''.join(exc_lines) - os.write(errpipe_write, pickle.dumps(exc_value)) - - # This exitcode won't be reported to applications, so it - # really doesn't matter what we return. - os._exit(255) - - # Parent - os.close(errpipe_write) - if p2cread and p2cwrite: - os.close(p2cread) - if c2pwrite and c2pread: - os.close(c2pwrite) - if errwrite and errread: - os.close(errwrite) - - # Wait for exec to fail or succeed; possibly raising exception - data = os.read(errpipe_read, 1048576) # Exceptions limited to 1 MB - os.close(errpipe_read) - if data != "": - os.waitpid(self.pid, 0) - child_exception = pickle.loads(data) - raise child_exception - - - def _handle_exitstatus(self, sts): - if os.WIFSIGNALED(sts): - self.returncode = -os.WTERMSIG(sts) - elif os.WIFEXITED(sts): - self.returncode = os.WEXITSTATUS(sts) - else: - # Should never happen - raise RuntimeError("Unknown child exit status!") - - - def poll(self, _deadstate=None): - """Check if child process has terminated. Returns returncode - attribute.""" - if self.returncode is None: - try: - pid, sts = os.waitpid(self.pid, os.WNOHANG) - if pid == self.pid: - self._handle_exitstatus(sts) - except os.error: - if _deadstate is not None: - self.returncode = _deadstate - return self.returncode - - - def wait(self): - """Wait for child process to terminate. Returns returncode - attribute.""" - if self.returncode is None: - pid, sts = os.waitpid(self.pid, 0) - self._handle_exitstatus(sts) - return self.returncode - - - def _communicate(self, input): - read_set = [] - write_set = [] - stdout = None # Return - stderr = None # Return - - if self.stdin: - # Flush stdio buffer. This might block, if the user has - # been writing to .stdin in an uncontrolled fashion. - self.stdin.flush() - if input: - write_set.append(self.stdin) - else: - self.stdin.close() - if self.stdout: - read_set.append(self.stdout) - stdout = [] - if self.stderr: - read_set.append(self.stderr) - stderr = [] - - input_offset = 0 - while read_set or write_set: - rlist, wlist, xlist = select.select(read_set, write_set, []) - - if self.stdin in wlist: - # When select has indicated that the file is writable, - # we can write up to PIPE_BUF bytes without risk - # blocking. POSIX defines PIPE_BUF >= 512 - m = memoryview(input)[input_offset:input_offset+512] - bytes_written = os.write(self.stdin.fileno(), m) - input_offset = input_offset + bytes_written - if input_offset >= len(input): - self.stdin.close() - write_set.remove(self.stdin) - - if self.stdout in rlist: - data = os.read(self.stdout.fileno(), 1024) - if data == "": - self.stdout.close() - read_set.remove(self.stdout) - stdout.append(data) - - if self.stderr in rlist: - data = os.read(self.stderr.fileno(), 1024) - if data == "": - self.stderr.close() - read_set.remove(self.stderr) - stderr.append(data) - - # All data exchanged. Translate lists into strings. - if stdout is not None: - stdout = ''.join(stdout) - if stderr is not None: - stderr = ''.join(stderr) - - # Translate newlines, if requested. We cannot let the file - # object do the translation: It is based on stdio, which is - # impossible to combine with select (unless forcing no - # buffering). - if self.universal_newlines and hasattr(file, 'newlines'): - if stdout: - stdout = self._translate_newlines(stdout) - if stderr: - stderr = self._translate_newlines(stderr) - - self.wait() - return (stdout, stderr) - - -def _demo_posix(): - # - # Example 1: Simple redirection: Get process list - # - plist = Popen(["ps"], stdout=PIPE).communicate()[0] - print "Process list:" - print plist - - # - # Example 2: Change uid before executing child - # - if os.getuid() == 0: - p = Popen(["id"], preexec_fn=lambda: os.setuid(100)) - p.wait() - - # - # Example 3: Connecting several subprocesses - # - print "Looking for 'hda'..." - p1 = Popen(["dmesg"], stdout=PIPE) - p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) - print repr(p2.communicate()[0]) - - # - # Example 4: Catch execution error - # - print - print "Trying a weird file..." - try: - print Popen(["/this/path/does/not/exist"]).communicate() - except OSError, e: - if e.errno == errno.ENOENT: - print "The file didn't exist. I thought so..." - print "Child traceback:" - print e.child_traceback - else: - print "Error", e.errno - else: - sys.stderr.write( "Gosh. No error.\n" ) - - -def _demo_windows(): - # - # Example 1: Connecting several subprocesses - # - print "Looking for 'PROMPT' in set output..." - p1 = Popen("set", stdout=PIPE, shell=True) - p2 = Popen('find "PROMPT"', stdin=p1.stdout, stdout=PIPE) - print repr(p2.communicate()[0]) - - # - # Example 2: Simple execution of program - # - print "Executing calc..." - p = Popen("calc") - p.wait() - - -if __name__ == "__main__": - if mswindows: - _demo_windows() - else: - _demo_posix() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/cpp.py b/scons/scons-local-2.2.0/SCons/cpp.py deleted file mode 100644 index 74fff7c70..000000000 --- a/scons/scons-local-2.2.0/SCons/cpp.py +++ /dev/null @@ -1,589 +0,0 @@ -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/cpp.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - -__doc__ = """ -SCons C Pre-Processor module -""" -#TODO 2.3 and before has no sorted() -import SCons.compat - -import os -import re - -# -# First "subsystem" of regular expressions that we set up: -# -# Stuff to turn the C preprocessor directives in a file's contents into -# a list of tuples that we can process easily. -# - -# A table of regular expressions that fetch the arguments from the rest of -# a C preprocessor line. Different directives have different arguments -# that we want to fetch, using the regular expressions to which the lists -# of preprocessor directives map. -cpp_lines_dict = { - # Fetch the rest of a #if/#elif/#ifdef/#ifndef as one argument, - # separated from the keyword by white space. - ('if', 'elif', 'ifdef', 'ifndef',) - : '\s+(.+)', - - # Fetch the rest of a #import/#include/#include_next line as one - # argument, with white space optional. - ('import', 'include', 'include_next',) - : '\s*(.+)', - - # We don't care what comes after a #else or #endif line. - ('else', 'endif',) : '', - - # Fetch three arguments from a #define line: - # 1) The #defined keyword. - # 2) The optional parentheses and arguments (if it's a function-like - # macro, '' if it's not). - # 3) The expansion value. - ('define',) : '\s+([_A-Za-z][_A-Za-z0-9_]*)(\([^)]*\))?\s*(.*)', - - # Fetch the #undefed keyword from a #undef line. - ('undef',) : '\s+([_A-Za-z][A-Za-z0-9_]*)', -} - -# Create a table that maps each individual C preprocessor directive to -# the corresponding compiled regular expression that fetches the arguments -# we care about. -Table = {} -for op_list, expr in cpp_lines_dict.items(): - e = re.compile(expr) - for op in op_list: - Table[op] = e -del e -del op -del op_list - -# Create a list of the expressions we'll use to match all of the -# preprocessor directives. These are the same as the directives -# themselves *except* that we must use a negative lookahead assertion -# when matching "if" so it doesn't match the "if" in "ifdef." -override = { - 'if' : 'if(?!def)', -} -l = [override.get(x, x) for x in Table.keys()] - - -# Turn the list of expressions into one big honkin' regular expression -# that will match all the preprocessor lines at once. This will return -# a list of tuples, one for each preprocessor line. The preprocessor -# directive will be the first element in each tuple, and the rest of -# the line will be the second element. -e = '^\s*#\s*(' + '|'.join(l) + ')(.*)$' - -# And last but not least, compile the expression. -CPP_Expression = re.compile(e, re.M) - - - - -# -# Second "subsystem" of regular expressions that we set up: -# -# Stuff to translate a C preprocessor expression (as found on a #if or -# #elif line) into an equivalent Python expression that we can eval(). -# - -# A dictionary that maps the C representation of Boolean operators -# to their Python equivalents. -CPP_to_Python_Ops_Dict = { - '!' : ' not ', - '!=' : ' != ', - '&&' : ' and ', - '||' : ' or ', - '?' : ' and ', - ':' : ' or ', - '\r' : '', -} - -CPP_to_Python_Ops_Sub = lambda m: CPP_to_Python_Ops_Dict[m.group(0)] - -# We have to sort the keys by length so that longer expressions -# come *before* shorter expressions--in particular, "!=" must -# come before "!" in the alternation. Without this, the Python -# re module, as late as version 2.2.2, empirically matches the -# "!" in "!=" first, instead of finding the longest match. -# What's up with that? -l = sorted(CPP_to_Python_Ops_Dict.keys(), key=lambda a: len(a), reverse=True) - -# Turn the list of keys into one regular expression that will allow us -# to substitute all of the operators at once. -expr = '|'.join(map(re.escape, l)) - -# ...and compile the expression. -CPP_to_Python_Ops_Expression = re.compile(expr) - -# A separate list of expressions to be evaluated and substituted -# sequentially, not all at once. -CPP_to_Python_Eval_List = [ - ['defined\s+(\w+)', '"\\1" in __dict__'], - ['defined\s*\((\w+)\)', '"\\1" in __dict__'], - ['/\*.*\*/', ''], - ['/\*.*', ''], - ['//.*', ''], - ['(0x[0-9A-Fa-f]*)[UL]+', '\\1'], -] - -# Replace the string representations of the regular expressions in the -# list with compiled versions. -for l in CPP_to_Python_Eval_List: - l[0] = re.compile(l[0]) - -# Wrap up all of the above into a handy function. -def CPP_to_Python(s): - """ - Converts a C pre-processor expression into an equivalent - Python expression that can be evaluated. - """ - s = CPP_to_Python_Ops_Expression.sub(CPP_to_Python_Ops_Sub, s) - for expr, repl in CPP_to_Python_Eval_List: - s = expr.sub(repl, s) - return s - - - -del expr -del l -del override - - - -class FunctionEvaluator(object): - """ - Handles delayed evaluation of a #define function call. - """ - def __init__(self, name, args, expansion): - """ - Squirrels away the arguments and expansion value of a #define - macro function for later evaluation when we must actually expand - a value that uses it. - """ - self.name = name - self.args = function_arg_separator.split(args) - try: - expansion = expansion.split('##') - except AttributeError: - pass - self.expansion = expansion - def __call__(self, *values): - """ - Evaluates the expansion of a #define macro function called - with the specified values. - """ - if len(self.args) != len(values): - raise ValueError("Incorrect number of arguments to `%s'" % self.name) - # Create a dictionary that maps the macro arguments to the - # corresponding values in this "call." We'll use this when we - # eval() the expansion so that arguments will get expanded to - # the right values. - locals = {} - for k, v in zip(self.args, values): - locals[k] = v - - parts = [] - for s in self.expansion: - if not s in self.args: - s = repr(s) - parts.append(s) - statement = ' + '.join(parts) - - return eval(statement, globals(), locals) - - - -# Find line continuations. -line_continuations = re.compile('\\\\\r?\n') - -# Search for a "function call" macro on an expansion. Returns the -# two-tuple of the "function" name itself, and a string containing the -# arguments within the call parentheses. -function_name = re.compile('(\S+)\(([^)]*)\)') - -# Split a string containing comma-separated function call arguments into -# the separate arguments. -function_arg_separator = re.compile(',\s*') - - - -class PreProcessor(object): - """ - The main workhorse class for handling C pre-processing. - """ - def __init__(self, current=os.curdir, cpppath=(), dict={}, all=0): - global Table - - cpppath = tuple(cpppath) - - self.searchpath = { - '"' : (current,) + cpppath, - '<' : cpppath + (current,), - } - - # Initialize our C preprocessor namespace for tracking the - # values of #defined keywords. We use this namespace to look - # for keywords on #ifdef/#ifndef lines, and to eval() the - # expressions on #if/#elif lines (after massaging them from C to - # Python). - self.cpp_namespace = dict.copy() - self.cpp_namespace['__dict__'] = self.cpp_namespace - - if all: - self.do_include = self.all_include - - # For efficiency, a dispatch table maps each C preprocessor - # directive (#if, #define, etc.) to the method that should be - # called when we see it. We accomodate state changes (#if, - # #ifdef, #ifndef) by pushing the current dispatch table on a - # stack and changing what method gets called for each relevant - # directive we might see next at this level (#else, #elif). - # #endif will simply pop the stack. - d = { - 'scons_current_file' : self.scons_current_file - } - for op in Table.keys(): - d[op] = getattr(self, 'do_' + op) - self.default_table = d - - # Controlling methods. - - def tupleize(self, contents): - """ - Turns the contents of a file into a list of easily-processed - tuples describing the CPP lines in the file. - - The first element of each tuple is the line's preprocessor - directive (#if, #include, #define, etc., minus the initial '#'). - The remaining elements are specific to the type of directive, as - pulled apart by the regular expression. - """ - global CPP_Expression, Table - contents = line_continuations.sub('', contents) - cpp_tuples = CPP_Expression.findall(contents) - return [(m[0],) + Table[m[0]].match(m[1]).groups() for m in cpp_tuples] - - def __call__(self, file): - """ - Pre-processes a file. - - This is the main public entry point. - """ - self.current_file = file - return self.process_contents(self.read_file(file), file) - - def process_contents(self, contents, fname=None): - """ - Pre-processes a file contents. - - This is the main internal entry point. - """ - self.stack = [] - self.dispatch_table = self.default_table.copy() - self.current_file = fname - self.tuples = self.tupleize(contents) - - self.initialize_result(fname) - while self.tuples: - t = self.tuples.pop(0) - # Uncomment to see the list of tuples being processed (e.g., - # to validate the CPP lines are being translated correctly). - #print t - self.dispatch_table[t[0]](t) - return self.finalize_result(fname) - - # Dispatch table stack manipulation methods. - - def save(self): - """ - Pushes the current dispatch table on the stack and re-initializes - the current dispatch table to the default. - """ - self.stack.append(self.dispatch_table) - self.dispatch_table = self.default_table.copy() - - def restore(self): - """ - Pops the previous dispatch table off the stack and makes it the - current one. - """ - try: self.dispatch_table = self.stack.pop() - except IndexError: pass - - # Utility methods. - - def do_nothing(self, t): - """ - Null method for when we explicitly want the action for a - specific preprocessor directive to do nothing. - """ - pass - - def scons_current_file(self, t): - self.current_file = t[1] - - def eval_expression(self, t): - """ - Evaluates a C preprocessor expression. - - This is done by converting it to a Python equivalent and - eval()ing it in the C preprocessor namespace we use to - track #define values. - """ - t = CPP_to_Python(' '.join(t[1:])) - try: return eval(t, self.cpp_namespace) - except (NameError, TypeError): return 0 - - def initialize_result(self, fname): - self.result = [fname] - - def finalize_result(self, fname): - return self.result[1:] - - def find_include_file(self, t): - """ - Finds the #include file for a given preprocessor tuple. - """ - fname = t[2] - for d in self.searchpath[t[1]]: - if d == os.curdir: - f = fname - else: - f = os.path.join(d, fname) - if os.path.isfile(f): - return f - return None - - def read_file(self, file): - return open(file).read() - - # Start and stop processing include lines. - - def start_handling_includes(self, t=None): - """ - Causes the PreProcessor object to start processing #import, - #include and #include_next lines. - - This method will be called when a #if, #ifdef, #ifndef or #elif - evaluates True, or when we reach the #else in a #if, #ifdef, - #ifndef or #elif block where a condition already evaluated - False. - - """ - d = self.dispatch_table - d['import'] = self.do_import - d['include'] = self.do_include - d['include_next'] = self.do_include - - def stop_handling_includes(self, t=None): - """ - Causes the PreProcessor object to stop processing #import, - #include and #include_next lines. - - This method will be called when a #if, #ifdef, #ifndef or #elif - evaluates False, or when we reach the #else in a #if, #ifdef, - #ifndef or #elif block where a condition already evaluated True. - """ - d = self.dispatch_table - d['import'] = self.do_nothing - d['include'] = self.do_nothing - d['include_next'] = self.do_nothing - - # Default methods for handling all of the preprocessor directives. - # (Note that what actually gets called for a given directive at any - # point in time is really controlled by the dispatch_table.) - - def _do_if_else_condition(self, condition): - """ - Common logic for evaluating the conditions on #if, #ifdef and - #ifndef lines. - """ - self.save() - d = self.dispatch_table - if condition: - self.start_handling_includes() - d['elif'] = self.stop_handling_includes - d['else'] = self.stop_handling_includes - else: - self.stop_handling_includes() - d['elif'] = self.do_elif - d['else'] = self.start_handling_includes - - def do_ifdef(self, t): - """ - Default handling of a #ifdef line. - """ - self._do_if_else_condition(t[1] in self.cpp_namespace) - - def do_ifndef(self, t): - """ - Default handling of a #ifndef line. - """ - self._do_if_else_condition(t[1] not in self.cpp_namespace) - - def do_if(self, t): - """ - Default handling of a #if line. - """ - self._do_if_else_condition(self.eval_expression(t)) - - def do_elif(self, t): - """ - Default handling of a #elif line. - """ - d = self.dispatch_table - if self.eval_expression(t): - self.start_handling_includes() - d['elif'] = self.stop_handling_includes - d['else'] = self.stop_handling_includes - - def do_else(self, t): - """ - Default handling of a #else line. - """ - pass - - def do_endif(self, t): - """ - Default handling of a #endif line. - """ - self.restore() - - def do_define(self, t): - """ - Default handling of a #define line. - """ - _, name, args, expansion = t - try: - expansion = int(expansion) - except (TypeError, ValueError): - pass - if args: - evaluator = FunctionEvaluator(name, args[1:-1], expansion) - self.cpp_namespace[name] = evaluator - else: - self.cpp_namespace[name] = expansion - - def do_undef(self, t): - """ - Default handling of a #undef line. - """ - try: del self.cpp_namespace[t[1]] - except KeyError: pass - - def do_import(self, t): - """ - Default handling of a #import line. - """ - # XXX finish this -- maybe borrow/share logic from do_include()...? - pass - - def do_include(self, t): - """ - Default handling of a #include line. - """ - t = self.resolve_include(t) - include_file = self.find_include_file(t) - if include_file: - #print "include_file =", include_file - self.result.append(include_file) - contents = self.read_file(include_file) - new_tuples = [('scons_current_file', include_file)] + \ - self.tupleize(contents) + \ - [('scons_current_file', self.current_file)] - self.tuples[:] = new_tuples + self.tuples - - # Date: Tue, 22 Nov 2005 20:26:09 -0500 - # From: Stefan Seefeld - # - # By the way, #include_next is not the same as #include. The difference - # being that #include_next starts its search in the path following the - # path that let to the including file. In other words, if your system - # include paths are ['/foo', '/bar'], and you are looking at a header - # '/foo/baz.h', it might issue an '#include_next ' which would - # correctly resolve to '/bar/baz.h' (if that exists), but *not* see - # '/foo/baz.h' again. See http://www.delorie.com/gnu/docs/gcc/cpp_11.html - # for more reasoning. - # - # I have no idea in what context 'import' might be used. - - # XXX is #include_next really the same as #include ? - do_include_next = do_include - - # Utility methods for handling resolution of include files. - - def resolve_include(self, t): - """Resolve a tuple-ized #include line. - - This handles recursive expansion of values without "" or <> - surrounding the name until an initial " or < is found, to handle - #include FILE - where FILE is a #define somewhere else. - """ - s = t[1] - while not s[0] in '<"': - #print "s =", s - try: - s = self.cpp_namespace[s] - except KeyError: - m = function_name.search(s) - s = self.cpp_namespace[m.group(1)] - if callable(s): - args = function_arg_separator.split(m.group(2)) - s = s(*args) - if not s: - return None - return (t[0], s[0], s[1:-1]) - - def all_include(self, t): - """ - """ - self.result.append(self.resolve_include(t)) - -class DumbPreProcessor(PreProcessor): - """A preprocessor that ignores all #if/#elif/#else/#endif directives - and just reports back *all* of the #include files (like the classic - SCons scanner did). - - This is functionally equivalent to using a regular expression to - find all of the #include lines, only slower. It exists mainly as - an example of how the main PreProcessor class can be sub-classed - to tailor its behavior. - """ - def __init__(self, *args, **kw): - PreProcessor.__init__(self, *args, **kw) - d = self.default_table - for func in ['if', 'elif', 'else', 'endif', 'ifdef', 'ifndef']: - d[func] = d[func] = self.do_nothing - -del __revision__ - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/dblite.py b/scons/scons-local-2.2.0/SCons/dblite.py deleted file mode 100644 index f4ba90a1f..000000000 --- a/scons/scons-local-2.2.0/SCons/dblite.py +++ /dev/null @@ -1,254 +0,0 @@ -# dblite.py module contributed by Ralf W. Grosse-Kunstleve. -# Extended for Unicode by Steven Knight. - -import SCons.compat - -import builtins -import os -# compat layer imports "cPickle" for us if it's available. -import pickle -import shutil -import time - -keep_all_files = 00000 -ignore_corrupt_dbfiles = 0 - -def corruption_warning(filename): - print "Warning: Discarding corrupt database:", filename - -try: unicode -except NameError: - def is_string(s): - return isinstance(s, str) -else: - def is_string(s): - return type(s) in (str, unicode) - -try: - unicode('a') -except NameError: - def unicode(s): return s - -dblite_suffix = '.dblite' -tmp_suffix = '.tmp' - -class dblite(object): - - # Squirrel away references to the functions in various modules - # that we'll use when our __del__() method calls our sync() method - # during shutdown. We might get destroyed when Python is in the midst - # of tearing down the different modules we import in an essentially - # arbitrary order, and some of the various modules's global attributes - # may already be wiped out from under us. - # - # See the discussion at: - # http://mail.python.org/pipermail/python-bugs-list/2003-March/016877.html - - _open = builtins.open - _pickle_dump = staticmethod(pickle.dump) - _os_chmod = os.chmod - try: - _os_chown = os.chown - except AttributeError: - _os_chown = None - _os_rename = os.rename - _os_unlink = os.unlink - _shutil_copyfile = shutil.copyfile - _time_time = time.time - - def __init__(self, file_base_name, flag, mode): - assert flag in (None, "r", "w", "c", "n") - if (flag is None): flag = "r" - base, ext = os.path.splitext(file_base_name) - if ext == dblite_suffix: - # There's already a suffix on the file name, don't add one. - self._file_name = file_base_name - self._tmp_name = base + tmp_suffix - else: - self._file_name = file_base_name + dblite_suffix - self._tmp_name = file_base_name + tmp_suffix - self._flag = flag - self._mode = mode - self._dict = {} - self._needs_sync = 00000 - if self._os_chown is not None and (os.geteuid()==0 or os.getuid()==0): - # running as root; chown back to current owner/group when done - try: - statinfo = os.stat(self._file_name) - self._chown_to = statinfo.st_uid - self._chgrp_to = statinfo.st_gid - except OSError, e: - # db file doesn't exist yet. - # Check os.environ for SUDO_UID, use if set - self._chown_to = int(os.environ.get('SUDO_UID', -1)) - self._chgrp_to = int(os.environ.get('SUDO_GID', -1)) - else: - self._chown_to = -1 # don't chown - self._chgrp_to = -1 # don't chgrp - if (self._flag == "n"): - self._open(self._file_name, "wb", self._mode) - else: - try: - f = self._open(self._file_name, "rb") - except IOError, e: - if (self._flag != "c"): - raise e - self._open(self._file_name, "wb", self._mode) - else: - p = f.read() - if (len(p) > 0): - try: - self._dict = pickle.loads(p) - except (pickle.UnpicklingError, EOFError): - if (ignore_corrupt_dbfiles == 0): raise - if (ignore_corrupt_dbfiles == 1): - corruption_warning(self._file_name) - - def close(self): - if (self._needs_sync): - self.sync() - - def __del__(self): - self.close() - - def sync(self): - self._check_writable() - f = self._open(self._tmp_name, "wb", self._mode) - self._pickle_dump(self._dict, f, 1) - f.close() - # Windows doesn't allow renaming if the file exists, so unlink - # it first, chmod'ing it to make sure we can do so. On UNIX, we - # may not be able to chmod the file if it's owned by someone else - # (e.g. from a previous run as root). We should still be able to - # unlink() the file if the directory's writable, though, so ignore - # any OSError exception thrown by the chmod() call. - try: self._os_chmod(self._file_name, 0777) - except OSError: pass - self._os_unlink(self._file_name) - self._os_rename(self._tmp_name, self._file_name) - if self._os_chown is not None and self._chown_to > 0: # don't chown to root or -1 - try: - self._os_chown(self._file_name, self._chown_to, self._chgrp_to) - except OSError: - pass - self._needs_sync = 00000 - if (keep_all_files): - self._shutil_copyfile( - self._file_name, - self._file_name + "_" + str(int(self._time_time()))) - - def _check_writable(self): - if (self._flag == "r"): - raise IOError("Read-only database: %s" % self._file_name) - - def __getitem__(self, key): - return self._dict[key] - - def __setitem__(self, key, value): - self._check_writable() - if (not is_string(key)): - raise TypeError("key `%s' must be a string but is %s" % (key, type(key))) - if (not is_string(value)): - raise TypeError("value `%s' must be a string but is %s" % (value, type(value))) - self._dict[key] = value - self._needs_sync = 0001 - - def keys(self): - return list(self._dict.keys()) - - def has_key(self, key): - return key in self._dict - - def __contains__(self, key): - return key in self._dict - - def iterkeys(self): - # Wrapping name in () prevents fixer from "fixing" this - return (self._dict.iterkeys)() - - __iter__ = iterkeys - - def __len__(self): - return len(self._dict) - -def open(file, flag=None, mode=0666): - return dblite(file, flag, mode) - -def _exercise(): - db = open("tmp", "n") - assert len(db) == 0 - db["foo"] = "bar" - assert db["foo"] == "bar" - db[unicode("ufoo")] = unicode("ubar") - assert db[unicode("ufoo")] == unicode("ubar") - db.sync() - db = open("tmp", "c") - assert len(db) == 2, len(db) - assert db["foo"] == "bar" - db["bar"] = "foo" - assert db["bar"] == "foo" - db[unicode("ubar")] = unicode("ufoo") - assert db[unicode("ubar")] == unicode("ufoo") - db.sync() - db = open("tmp", "r") - assert len(db) == 4, len(db) - assert db["foo"] == "bar" - assert db["bar"] == "foo" - assert db[unicode("ufoo")] == unicode("ubar") - assert db[unicode("ubar")] == unicode("ufoo") - try: - db.sync() - except IOError, e: - assert str(e) == "Read-only database: tmp.dblite" - else: - raise RuntimeError("IOError expected.") - db = open("tmp", "w") - assert len(db) == 4 - db["ping"] = "pong" - db.sync() - try: - db[(1,2)] = "tuple" - except TypeError, e: - assert str(e) == "key `(1, 2)' must be a string but is ", str(e) - else: - raise RuntimeError("TypeError exception expected") - try: - db["list"] = [1,2] - except TypeError, e: - assert str(e) == "value `[1, 2]' must be a string but is ", str(e) - else: - raise RuntimeError("TypeError exception expected") - db = open("tmp", "r") - assert len(db) == 5 - db = open("tmp", "n") - assert len(db) == 0 - dblite._open("tmp.dblite", "w") - db = open("tmp", "r") - dblite._open("tmp.dblite", "w").write("x") - try: - db = open("tmp", "r") - except pickle.UnpicklingError: - pass - else: - raise RuntimeError("pickle exception expected.") - global ignore_corrupt_dbfiles - ignore_corrupt_dbfiles = 2 - db = open("tmp", "r") - assert len(db) == 0 - os.unlink("tmp.dblite") - try: - db = open("tmp", "w") - except IOError, e: - assert str(e) == "[Errno 2] No such file or directory: 'tmp.dblite'", str(e) - else: - raise RuntimeError("IOError expected.") - print "OK" - -if (__name__ == "__main__"): - _exercise() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/SCons/exitfuncs.py b/scons/scons-local-2.2.0/SCons/exitfuncs.py deleted file mode 100644 index 4e604e193..000000000 --- a/scons/scons-local-2.2.0/SCons/exitfuncs.py +++ /dev/null @@ -1,77 +0,0 @@ -"""SCons.exitfuncs - -Register functions which are executed when SCons exits for any reason. - -""" - -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation -# -# 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. -# - -__revision__ = "src/engine/SCons/exitfuncs.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" - - - -_exithandlers = [] -def _run_exitfuncs(): - """run any registered exit functions - - _exithandlers is traversed in reverse order so functions are executed - last in, first out. - """ - - while _exithandlers: - func, targs, kargs = _exithandlers.pop() - func(*targs, **kargs) - -def register(func, *targs, **kargs): - """register a function to be executed upon normal program termination - - func - function to be called at exit - targs - optional arguments to pass to func - kargs - optional keyword arguments to pass to func - """ - _exithandlers.append((func, targs, kargs)) - -import sys - -try: - x = sys.exitfunc - - # if x isn't our own exit func executive, assume it's another - # registered exit function - append it to our list... - if x != _run_exitfuncs: - register(x) - -except AttributeError: - pass - -# make our exit function get run by python when it exits: -sys.exitfunc = _run_exitfuncs - -del sys - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.2.0/scons-2.2.0.egg-info b/scons/scons-local-2.2.0/scons-2.2.0.egg-info deleted file mode 100644 index b8f8d5e88..000000000 --- a/scons/scons-local-2.2.0/scons-2.2.0.egg-info +++ /dev/null @@ -1,13 +0,0 @@ -Metadata-Version: 1.0 -Name: scons -Version: 2.2.0 -Summary: Open Source next-generation build tool. -Home-page: http://www.scons.org/ -Author: Steven Knight -Author-email: knight@baldmt.com -License: UNKNOWN -Description: Open Source next-generation build tool. - Improved, cross-platform substitute for the classic Make - utility. In short, SCons is an easier, more reliable - and faster way to build software. -Platform: UNKNOWN diff --git a/scons/scons-time.py b/scons/scons-time.py index 1d774cb22..2b67410ec 100755 --- a/scons/scons-time.py +++ b/scons/scons-time.py @@ -9,7 +9,7 @@ # # -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -32,7 +32,7 @@ from __future__ import division from __future__ import nested_scopes -__revision__ = "src/script/scons-time.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" +__revision__ = "src/script/scons-time.py 2013/03/03 09:48:35 garyo" import getopt import glob diff --git a/scons/scons.py b/scons/scons.py index 24686385c..466c72833 100755 --- a/scons/scons.py +++ b/scons/scons.py @@ -2,7 +2,7 @@ # # SCons - a Software Constructor # -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -23,15 +23,15 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -__revision__ = "src/script/scons.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" +__revision__ = "src/script/scons.py 2013/03/03 09:48:35 garyo" -__version__ = "2.2.0" +__version__ = "2.3.0" -__build__ = "issue-2856:2676:d23b7a2f45e8[MODIFIED]" +__build__ = "" -__buildsys__ = "oberbrunner-dev" +__buildsys__ = "reepicheep" -__date__ = "2012/08/05 15:38:28" +__date__ = "2013/03/03 09:48:35" __developer__ = "garyo" @@ -55,18 +55,12 @@ import sys # engine modules if they're in either directory. -# Check to see if the python version is > 3.0 which is currently unsupported -# If so exit with error message -try: - if sys.version_info >= (3,0,0): - msg = "scons: *** SCons version %s does not run under Python version %s.\n\ -Python 3.0 and later are not yet supported.\n" - sys.stderr.write(msg % (__version__, sys.version.split()[0])) - sys.exit(1) -except AttributeError: - # Pre-1.6 Python has no sys.version_info - # No need to check version as we then know the version is < 3.0.0 and supported - pass +if sys.version_info >= (3,0,0): + msg = "scons: *** SCons version %s does not run under Python version %s.\n\ +Python 3 is not yet supported.\n" + sys.stderr.write(msg % (__version__, sys.version.split()[0])) + sys.exit(1) + script_dir = sys.path[0] @@ -184,7 +178,15 @@ sys.path = libs + sys.path ############################################################################## if __name__ == "__main__": - import SCons.Script + try: + import SCons.Script + except: + ROOT = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..', 'engine') + if os.path.exists(ROOT): + sys.path += [ROOT] + print("SCons import failed. Trying to run from source directory") + import SCons.Script + # this does all the work, and calls sys.exit # with the proper exit status when done. SCons.Script.main() diff --git a/scons/sconsign.py b/scons/sconsign.py index 6e8df4e94..99f0ee83d 100755 --- a/scons/sconsign.py +++ b/scons/sconsign.py @@ -2,7 +2,7 @@ # # SCons - a Software Constructor # -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -23,15 +23,15 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -__revision__ = "src/script/sconsign.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" +__revision__ = "src/script/sconsign.py 2013/03/03 09:48:35 garyo" -__version__ = "2.2.0" +__version__ = "2.3.0" -__build__ = "issue-2856:2676:d23b7a2f45e8[MODIFIED]" +__build__ = "" -__buildsys__ = "oberbrunner-dev" +__buildsys__ = "reepicheep" -__date__ = "2012/08/05 15:38:28" +__date__ = "2013/03/03 09:48:35" __developer__ = "garyo" From a6ef710f747c958622f61b2270e0e360ddffef08 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 21 May 2013 21:01:07 -0700 Subject: [PATCH 068/110] Add file I should have added in 8634a04938bb0b0bd668dff2bfbe2ac096711f5c --- src/debug_symbolizer.cpp | 57 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/debug_symbolizer.cpp diff --git a/src/debug_symbolizer.cpp b/src/debug_symbolizer.cpp new file mode 100644 index 000000000..1b064dbca --- /dev/null +++ b/src/debug_symbolizer.cpp @@ -0,0 +1,57 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2013 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +// mapnik +#include +#include + +namespace mapnik +{ + +static const char * debug_symbolizer_mode_strings[] = { + "collision", + "vertex", + "" +}; + +IMPLEMENT_ENUM( debug_symbolizer_mode_e, debug_symbolizer_mode_strings ) + +debug_symbolizer::debug_symbolizer() +: symbolizer_base(), + mode_(DEBUG_SYM_MODE_COLLISION) {} + +debug_symbolizer::debug_symbolizer(debug_symbolizer const& rhs) + : symbolizer_base(rhs), + mode_(rhs.mode_) {} + +debug_symbolizer_mode_e debug_symbolizer::get_mode() const +{ + return mode_; +} + +void debug_symbolizer::set_mode(debug_symbolizer_mode_e mode) +{ + mode_ = mode; +} + +} + From 0060e4e773d6c8f11ead1aa6b2519b52e79023d4 Mon Sep 17 00:00:00 2001 From: artemp Date: Wed, 22 May 2013 10:28:13 +0100 Subject: [PATCH 069/110] + add missing scons-local-2.3.0 --- scons/scons-local-2.3.0/SCons/Action.py | 1257 +++++++ scons/scons-local-2.3.0/SCons/Builder.py | 877 +++++ scons/scons-local-2.3.0/SCons/CacheDir.py | 216 ++ scons/scons-local-2.3.0/SCons/Conftest.py | 793 ++++ scons/scons-local-2.3.0/SCons/Debug.py | 220 ++ scons/scons-local-2.3.0/SCons/Defaults.py | 494 +++ scons/scons-local-2.3.0/SCons/Environment.py | 2418 ++++++++++++ scons/scons-local-2.3.0/SCons/Errors.py | 205 + scons/scons-local-2.3.0/SCons/Executor.py | 633 ++++ scons/scons-local-2.3.0/SCons/Job.py | 435 +++ scons/scons-local-2.3.0/SCons/Memoize.py | 244 ++ scons/scons-local-2.3.0/SCons/Node/Alias.py | 152 + scons/scons-local-2.3.0/SCons/Node/FS.py | 3302 +++++++++++++++++ scons/scons-local-2.3.0/SCons/Node/Python.py | 128 + .../scons-local-2.3.0/SCons/Node/__init__.py | 1330 +++++++ .../SCons/Options/BoolOption.py | 50 + .../SCons/Options/EnumOption.py | 50 + .../SCons/Options/ListOption.py | 50 + .../SCons/Options/PackageOption.py | 50 + .../SCons/Options/PathOption.py | 76 + .../SCons/Options/__init__.py | 67 + scons/scons-local-2.3.0/SCons/PathList.py | 233 ++ .../SCons/Platform/__init__.py | 241 ++ scons/scons-local-2.3.0/SCons/Platform/aix.py | 69 + .../SCons/Platform/cygwin.py | 55 + .../SCons/Platform/darwin.py | 70 + .../scons-local-2.3.0/SCons/Platform/hpux.py | 46 + .../scons-local-2.3.0/SCons/Platform/irix.py | 44 + scons/scons-local-2.3.0/SCons/Platform/os2.py | 58 + .../scons-local-2.3.0/SCons/Platform/posix.py | 263 ++ .../scons-local-2.3.0/SCons/Platform/sunos.py | 50 + .../scons-local-2.3.0/SCons/Platform/win32.py | 415 +++ scons/scons-local-2.3.0/SCons/SConf.py | 1030 +++++ scons/scons-local-2.3.0/SCons/SConsign.py | 389 ++ scons/scons-local-2.3.0/SCons/Scanner/C.py | 132 + scons/scons-local-2.3.0/SCons/Scanner/D.py | 73 + scons/scons-local-2.3.0/SCons/Scanner/Dir.py | 109 + .../SCons/Scanner/Fortran.py | 316 ++ scons/scons-local-2.3.0/SCons/Scanner/IDL.py | 48 + .../scons-local-2.3.0/SCons/Scanner/LaTeX.py | 390 ++ scons/scons-local-2.3.0/SCons/Scanner/Prog.py | 101 + scons/scons-local-2.3.0/SCons/Scanner/RC.py | 55 + .../SCons/Scanner/__init__.py | 413 +++ .../SCons/Script/Interactive.py | 384 ++ scons/scons-local-2.3.0/SCons/Script/Main.py | 1407 +++++++ .../SCons/Script/SConsOptions.py | 946 +++++ .../SCons/Script/SConscript.py | 635 ++++ .../SCons/Script/__init__.py | 412 ++ scons/scons-local-2.3.0/SCons/Sig.py | 63 + scons/scons-local-2.3.0/SCons/Subst.py | 904 +++++ scons/scons-local-2.3.0/SCons/Taskmaster.py | 1032 ++++++ scons/scons-local-2.3.0/SCons/Tool/386asm.py | 61 + .../scons-local-2.3.0/SCons/Tool/BitKeeper.py | 67 + scons/scons-local-2.3.0/SCons/Tool/CVS.py | 73 + .../SCons/Tool/FortranCommon.py | 263 ++ .../SCons/Tool/GettextCommon.py | 430 +++ .../SCons/Tool/JavaCommon.py | 323 ++ .../SCons/Tool/MSCommon/__init__.py | 56 + .../SCons/Tool/MSCommon/arch.py | 61 + .../SCons/Tool/MSCommon/common.py | 242 ++ .../SCons/Tool/MSCommon/netframework.py | 82 + .../SCons/Tool/MSCommon/sdk.py | 391 ++ .../SCons/Tool/MSCommon/vc.py | 464 +++ .../SCons/Tool/MSCommon/vs.py | 553 +++ .../scons-local-2.3.0/SCons/Tool/Perforce.py | 103 + .../SCons/Tool/PharLapCommon.py | 137 + scons/scons-local-2.3.0/SCons/Tool/RCS.py | 64 + scons/scons-local-2.3.0/SCons/Tool/SCCS.py | 64 + .../SCons/Tool/Subversion.py | 71 + .../scons-local-2.3.0/SCons/Tool/__init__.py | 802 ++++ scons/scons-local-2.3.0/SCons/Tool/aixc++.py | 82 + scons/scons-local-2.3.0/SCons/Tool/aixcc.py | 74 + scons/scons-local-2.3.0/SCons/Tool/aixf77.py | 80 + scons/scons-local-2.3.0/SCons/Tool/aixlink.py | 76 + .../scons-local-2.3.0/SCons/Tool/applelink.py | 71 + scons/scons-local-2.3.0/SCons/Tool/ar.py | 63 + scons/scons-local-2.3.0/SCons/Tool/as.py | 78 + scons/scons-local-2.3.0/SCons/Tool/bcc32.py | 81 + scons/scons-local-2.3.0/SCons/Tool/c++.py | 99 + scons/scons-local-2.3.0/SCons/Tool/cc.py | 102 + scons/scons-local-2.3.0/SCons/Tool/cvf.py | 58 + scons/scons-local-2.3.0/SCons/Tool/default.py | 50 + scons/scons-local-2.3.0/SCons/Tool/dmd.py | 240 ++ scons/scons-local-2.3.0/SCons/Tool/dvi.py | 64 + scons/scons-local-2.3.0/SCons/Tool/dvipdf.py | 125 + scons/scons-local-2.3.0/SCons/Tool/dvips.py | 95 + scons/scons-local-2.3.0/SCons/Tool/f03.py | 63 + scons/scons-local-2.3.0/SCons/Tool/f77.py | 62 + scons/scons-local-2.3.0/SCons/Tool/f90.py | 62 + scons/scons-local-2.3.0/SCons/Tool/f95.py | 63 + .../SCons/Tool/filesystem.py | 98 + scons/scons-local-2.3.0/SCons/Tool/fortran.py | 62 + scons/scons-local-2.3.0/SCons/Tool/g++.py | 90 + scons/scons-local-2.3.0/SCons/Tool/g77.py | 73 + scons/scons-local-2.3.0/SCons/Tool/gas.py | 53 + scons/scons-local-2.3.0/SCons/Tool/gcc.py | 80 + scons/scons-local-2.3.0/SCons/Tool/gettext.py | 48 + .../scons-local-2.3.0/SCons/Tool/gfortran.py | 64 + scons/scons-local-2.3.0/SCons/Tool/gnulink.py | 62 + scons/scons-local-2.3.0/SCons/Tool/gs.py | 81 + scons/scons-local-2.3.0/SCons/Tool/hpc++.py | 84 + scons/scons-local-2.3.0/SCons/Tool/hpcc.py | 53 + scons/scons-local-2.3.0/SCons/Tool/hplink.py | 77 + scons/scons-local-2.3.0/SCons/Tool/icc.py | 59 + scons/scons-local-2.3.0/SCons/Tool/icl.py | 52 + scons/scons-local-2.3.0/SCons/Tool/ifl.py | 72 + scons/scons-local-2.3.0/SCons/Tool/ifort.py | 88 + scons/scons-local-2.3.0/SCons/Tool/ilink.py | 59 + scons/scons-local-2.3.0/SCons/Tool/ilink32.py | 60 + scons/scons-local-2.3.0/SCons/Tool/install.py | 460 +++ scons/scons-local-2.3.0/SCons/Tool/intelc.py | 552 +++ scons/scons-local-2.3.0/SCons/Tool/ipkg.py | 67 + scons/scons-local-2.3.0/SCons/Tool/jar.py | 116 + scons/scons-local-2.3.0/SCons/Tool/javac.py | 232 ++ scons/scons-local-2.3.0/SCons/Tool/javah.py | 137 + scons/scons-local-2.3.0/SCons/Tool/latex.py | 80 + scons/scons-local-2.3.0/SCons/Tool/lex.py | 97 + scons/scons-local-2.3.0/SCons/Tool/link.py | 185 + scons/scons-local-2.3.0/SCons/Tool/linkloc.py | 112 + scons/scons-local-2.3.0/SCons/Tool/m4.py | 63 + scons/scons-local-2.3.0/SCons/Tool/masm.py | 77 + scons/scons-local-2.3.0/SCons/Tool/midl.py | 88 + scons/scons-local-2.3.0/SCons/Tool/mingw.py | 180 + scons/scons-local-2.3.0/SCons/Tool/msgfmt.py | 108 + scons/scons-local-2.3.0/SCons/Tool/msginit.py | 120 + .../scons-local-2.3.0/SCons/Tool/msgmerge.py | 104 + scons/scons-local-2.3.0/SCons/Tool/mslib.py | 64 + scons/scons-local-2.3.0/SCons/Tool/mslink.py | 327 ++ scons/scons-local-2.3.0/SCons/Tool/mssdk.py | 50 + scons/scons-local-2.3.0/SCons/Tool/msvc.py | 278 ++ scons/scons-local-2.3.0/SCons/Tool/msvs.py | 1803 +++++++++ scons/scons-local-2.3.0/SCons/Tool/mwcc.py | 207 ++ scons/scons-local-2.3.0/SCons/Tool/mwld.py | 107 + scons/scons-local-2.3.0/SCons/Tool/nasm.py | 72 + .../SCons/Tool/packaging/__init__.py | 312 ++ .../SCons/Tool/packaging/ipk.py | 185 + .../SCons/Tool/packaging/msi.py | 527 +++ .../SCons/Tool/packaging/rpm.py | 357 ++ .../SCons/Tool/packaging/src_tarbz2.py | 43 + .../SCons/Tool/packaging/src_targz.py | 43 + .../SCons/Tool/packaging/src_zip.py | 43 + .../SCons/Tool/packaging/tarbz2.py | 44 + .../SCons/Tool/packaging/targz.py | 44 + .../SCons/Tool/packaging/zip.py | 44 + scons/scons-local-2.3.0/SCons/Tool/pdf.py | 78 + .../scons-local-2.3.0/SCons/Tool/pdflatex.py | 84 + scons/scons-local-2.3.0/SCons/Tool/pdftex.py | 109 + scons/scons-local-2.3.0/SCons/Tool/qt.py | 336 ++ scons/scons-local-2.3.0/SCons/Tool/rmic.py | 126 + scons/scons-local-2.3.0/SCons/Tool/rpcgen.py | 70 + scons/scons-local-2.3.0/SCons/Tool/rpm.py | 132 + .../scons-local-2.3.0/SCons/Tool/rpmutils.py | 533 +++ scons/scons-local-2.3.0/SCons/Tool/sgiar.py | 68 + scons/scons-local-2.3.0/SCons/Tool/sgic++.py | 58 + scons/scons-local-2.3.0/SCons/Tool/sgicc.py | 53 + scons/scons-local-2.3.0/SCons/Tool/sgilink.py | 62 + scons/scons-local-2.3.0/SCons/Tool/sunar.py | 67 + scons/scons-local-2.3.0/SCons/Tool/sunc++.py | 142 + scons/scons-local-2.3.0/SCons/Tool/suncc.py | 58 + scons/scons-local-2.3.0/SCons/Tool/sunf77.py | 63 + scons/scons-local-2.3.0/SCons/Tool/sunf90.py | 64 + scons/scons-local-2.3.0/SCons/Tool/sunf95.py | 64 + scons/scons-local-2.3.0/SCons/Tool/sunlink.py | 76 + scons/scons-local-2.3.0/SCons/Tool/swig.py | 183 + scons/scons-local-2.3.0/SCons/Tool/tar.py | 73 + scons/scons-local-2.3.0/SCons/Tool/tex.py | 986 +++++ .../scons-local-2.3.0/SCons/Tool/textfile.py | 175 + scons/scons-local-2.3.0/SCons/Tool/tlib.py | 53 + scons/scons-local-2.3.0/SCons/Tool/wix.py | 104 + .../scons-local-2.3.0/SCons/Tool/xgettext.py | 339 ++ scons/scons-local-2.3.0/SCons/Tool/yacc.py | 140 + scons/scons-local-2.3.0/SCons/Tool/zip.py | 99 + scons/scons-local-2.3.0/SCons/Util.py | 1492 ++++++++ .../SCons/Variables/BoolVariable.py | 89 + .../SCons/Variables/EnumVariable.py | 103 + .../SCons/Variables/ListVariable.py | 135 + .../SCons/Variables/PackageVariable.py | 106 + .../SCons/Variables/PathVariable.py | 147 + .../SCons/Variables/__init__.py | 312 ++ scons/scons-local-2.3.0/SCons/Warnings.py | 246 ++ scons/scons-local-2.3.0/SCons/__init__.py | 49 + .../SCons/compat/__init__.py | 237 ++ .../SCons/compat/_scons_builtins.py | 107 + .../SCons/compat/_scons_collections.py | 45 + .../SCons/compat/_scons_dbm.py | 45 + .../SCons/compat/_scons_hashlib.py | 76 + .../SCons/compat/_scons_io.py | 45 + .../SCons/compat/_scons_sets.py | 563 +++ .../SCons/compat/_scons_subprocess.py | 1281 +++++++ scons/scons-local-2.3.0/SCons/cpp.py | 589 +++ scons/scons-local-2.3.0/SCons/dblite.py | 254 ++ scons/scons-local-2.3.0/SCons/exitfuncs.py | 64 + scons/scons-local-2.3.0/scons-2.3.0.egg-info | 13 + 193 files changed, 48461 insertions(+) create mode 100644 scons/scons-local-2.3.0/SCons/Action.py create mode 100644 scons/scons-local-2.3.0/SCons/Builder.py create mode 100644 scons/scons-local-2.3.0/SCons/CacheDir.py create mode 100644 scons/scons-local-2.3.0/SCons/Conftest.py create mode 100644 scons/scons-local-2.3.0/SCons/Debug.py create mode 100644 scons/scons-local-2.3.0/SCons/Defaults.py create mode 100644 scons/scons-local-2.3.0/SCons/Environment.py create mode 100644 scons/scons-local-2.3.0/SCons/Errors.py create mode 100644 scons/scons-local-2.3.0/SCons/Executor.py create mode 100644 scons/scons-local-2.3.0/SCons/Job.py create mode 100644 scons/scons-local-2.3.0/SCons/Memoize.py create mode 100644 scons/scons-local-2.3.0/SCons/Node/Alias.py create mode 100644 scons/scons-local-2.3.0/SCons/Node/FS.py create mode 100644 scons/scons-local-2.3.0/SCons/Node/Python.py create mode 100644 scons/scons-local-2.3.0/SCons/Node/__init__.py create mode 100644 scons/scons-local-2.3.0/SCons/Options/BoolOption.py create mode 100644 scons/scons-local-2.3.0/SCons/Options/EnumOption.py create mode 100644 scons/scons-local-2.3.0/SCons/Options/ListOption.py create mode 100644 scons/scons-local-2.3.0/SCons/Options/PackageOption.py create mode 100644 scons/scons-local-2.3.0/SCons/Options/PathOption.py create mode 100644 scons/scons-local-2.3.0/SCons/Options/__init__.py create mode 100644 scons/scons-local-2.3.0/SCons/PathList.py create mode 100644 scons/scons-local-2.3.0/SCons/Platform/__init__.py create mode 100644 scons/scons-local-2.3.0/SCons/Platform/aix.py create mode 100644 scons/scons-local-2.3.0/SCons/Platform/cygwin.py create mode 100644 scons/scons-local-2.3.0/SCons/Platform/darwin.py create mode 100644 scons/scons-local-2.3.0/SCons/Platform/hpux.py create mode 100644 scons/scons-local-2.3.0/SCons/Platform/irix.py create mode 100644 scons/scons-local-2.3.0/SCons/Platform/os2.py create mode 100644 scons/scons-local-2.3.0/SCons/Platform/posix.py create mode 100644 scons/scons-local-2.3.0/SCons/Platform/sunos.py create mode 100644 scons/scons-local-2.3.0/SCons/Platform/win32.py create mode 100644 scons/scons-local-2.3.0/SCons/SConf.py create mode 100644 scons/scons-local-2.3.0/SCons/SConsign.py create mode 100644 scons/scons-local-2.3.0/SCons/Scanner/C.py create mode 100644 scons/scons-local-2.3.0/SCons/Scanner/D.py create mode 100644 scons/scons-local-2.3.0/SCons/Scanner/Dir.py create mode 100644 scons/scons-local-2.3.0/SCons/Scanner/Fortran.py create mode 100644 scons/scons-local-2.3.0/SCons/Scanner/IDL.py create mode 100644 scons/scons-local-2.3.0/SCons/Scanner/LaTeX.py create mode 100644 scons/scons-local-2.3.0/SCons/Scanner/Prog.py create mode 100644 scons/scons-local-2.3.0/SCons/Scanner/RC.py create mode 100644 scons/scons-local-2.3.0/SCons/Scanner/__init__.py create mode 100644 scons/scons-local-2.3.0/SCons/Script/Interactive.py create mode 100644 scons/scons-local-2.3.0/SCons/Script/Main.py create mode 100644 scons/scons-local-2.3.0/SCons/Script/SConsOptions.py create mode 100644 scons/scons-local-2.3.0/SCons/Script/SConscript.py create mode 100644 scons/scons-local-2.3.0/SCons/Script/__init__.py create mode 100644 scons/scons-local-2.3.0/SCons/Sig.py create mode 100644 scons/scons-local-2.3.0/SCons/Subst.py create mode 100644 scons/scons-local-2.3.0/SCons/Taskmaster.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/386asm.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/BitKeeper.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/CVS.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/FortranCommon.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/GettextCommon.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/JavaCommon.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/MSCommon/__init__.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/MSCommon/arch.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/MSCommon/common.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/MSCommon/netframework.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/MSCommon/sdk.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/MSCommon/vc.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/MSCommon/vs.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/Perforce.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/PharLapCommon.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/RCS.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/SCCS.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/Subversion.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/__init__.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/aixc++.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/aixcc.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/aixf77.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/aixlink.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/applelink.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/ar.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/as.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/bcc32.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/c++.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/cc.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/cvf.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/default.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/dmd.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/dvi.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/dvipdf.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/dvips.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/f03.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/f77.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/f90.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/f95.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/filesystem.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/fortran.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/g++.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/g77.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/gas.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/gcc.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/gettext.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/gfortran.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/gnulink.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/gs.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/hpc++.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/hpcc.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/hplink.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/icc.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/icl.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/ifl.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/ifort.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/ilink.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/ilink32.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/install.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/intelc.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/ipkg.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/jar.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/javac.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/javah.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/latex.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/lex.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/link.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/linkloc.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/m4.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/masm.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/midl.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/mingw.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/msgfmt.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/msginit.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/msgmerge.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/mslib.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/mslink.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/mssdk.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/msvc.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/msvs.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/mwcc.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/mwld.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/nasm.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/packaging/__init__.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/packaging/ipk.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/packaging/msi.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/packaging/rpm.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/packaging/src_tarbz2.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/packaging/src_targz.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/packaging/src_zip.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/packaging/tarbz2.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/packaging/targz.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/packaging/zip.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/pdf.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/pdflatex.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/pdftex.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/qt.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/rmic.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/rpcgen.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/rpm.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/rpmutils.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/sgiar.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/sgic++.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/sgicc.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/sgilink.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/sunar.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/sunc++.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/suncc.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/sunf77.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/sunf90.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/sunf95.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/sunlink.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/swig.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/tar.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/tex.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/textfile.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/tlib.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/wix.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/xgettext.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/yacc.py create mode 100644 scons/scons-local-2.3.0/SCons/Tool/zip.py create mode 100644 scons/scons-local-2.3.0/SCons/Util.py create mode 100644 scons/scons-local-2.3.0/SCons/Variables/BoolVariable.py create mode 100644 scons/scons-local-2.3.0/SCons/Variables/EnumVariable.py create mode 100644 scons/scons-local-2.3.0/SCons/Variables/ListVariable.py create mode 100644 scons/scons-local-2.3.0/SCons/Variables/PackageVariable.py create mode 100644 scons/scons-local-2.3.0/SCons/Variables/PathVariable.py create mode 100644 scons/scons-local-2.3.0/SCons/Variables/__init__.py create mode 100644 scons/scons-local-2.3.0/SCons/Warnings.py create mode 100644 scons/scons-local-2.3.0/SCons/__init__.py create mode 100644 scons/scons-local-2.3.0/SCons/compat/__init__.py create mode 100644 scons/scons-local-2.3.0/SCons/compat/_scons_builtins.py create mode 100644 scons/scons-local-2.3.0/SCons/compat/_scons_collections.py create mode 100644 scons/scons-local-2.3.0/SCons/compat/_scons_dbm.py create mode 100644 scons/scons-local-2.3.0/SCons/compat/_scons_hashlib.py create mode 100644 scons/scons-local-2.3.0/SCons/compat/_scons_io.py create mode 100644 scons/scons-local-2.3.0/SCons/compat/_scons_sets.py create mode 100644 scons/scons-local-2.3.0/SCons/compat/_scons_subprocess.py create mode 100644 scons/scons-local-2.3.0/SCons/cpp.py create mode 100644 scons/scons-local-2.3.0/SCons/dblite.py create mode 100644 scons/scons-local-2.3.0/SCons/exitfuncs.py create mode 100644 scons/scons-local-2.3.0/scons-2.3.0.egg-info diff --git a/scons/scons-local-2.3.0/SCons/Action.py b/scons/scons-local-2.3.0/SCons/Action.py new file mode 100644 index 000000000..0021df6e7 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Action.py @@ -0,0 +1,1257 @@ +"""SCons.Action + +This encapsulates information about executing any sort of action that +can build one or more target Nodes (typically files) from one or more +source Nodes (also typically files) given a specific Environment. + +The base class here is ActionBase. The base class supplies just a few +OO utility methods and some generic methods for displaying information +about an Action in response to the various commands that control printing. + +A second-level base class is _ActionAction. This extends ActionBase +by providing the methods that can be used to show and perform an +action. True Action objects will subclass _ActionAction; Action +factory class objects will subclass ActionBase. + +The heavy lifting is handled by subclasses for the different types of +actions we might execute: + + CommandAction + CommandGeneratorAction + FunctionAction + ListAction + +The subclasses supply the following public interface methods used by +other modules: + + __call__() + THE public interface, "calling" an Action object executes the + command or Python function. This also takes care of printing + a pre-substitution command for debugging purposes. + + get_contents() + Fetches the "contents" of an Action for signature calculation + plus the varlist. This is what gets MD5 checksummed to decide + if a target needs to be rebuilt because its action changed. + + genstring() + Returns a string representation of the Action *without* + command substitution, but allows a CommandGeneratorAction to + generate the right action based on the specified target, + source and env. This is used by the Signature subsystem + (through the Executor) to obtain an (imprecise) representation + of the Action operation for informative purposes. + + +Subclasses also supply the following methods for internal use within +this module: + + __str__() + Returns a string approximation of the Action; no variable + substitution is performed. + + execute() + The internal method that really, truly, actually handles the + execution of a command or Python function. This is used so + that the __call__() methods can take care of displaying any + pre-substitution representations, and *then* execute an action + without worrying about the specific Actions involved. + + get_presig() + Fetches the "contents" of a subclass for signature calculation. + The varlist is added to this to produce the Action's contents. + + strfunction() + Returns a substituted string representation of the Action. + This is used by the _ActionAction.show() command to display the + command/function that will be executed to generate the target(s). + +There is a related independent ActionCaller class that looks like a +regular Action, and which serves as a wrapper for arbitrary functions +that we want to let the user specify the arguments to now, but actually +execute later (when an out-of-date check determines that it's needed to +be executed, for example). Objects of this class are returned by an +ActionFactory class that provides a __call__() method as a convenient +way for wrapping up the functions. + +""" + +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Action.py 2013/03/03 09:48:35 garyo" + +import SCons.compat + +import dis +import os +# compat layer imports "cPickle" for us if it's available. +import pickle +import re +import sys +import subprocess + +from SCons.Debug import logInstanceCreation +import SCons.Errors +import SCons.Executor +import SCons.Util +import SCons.Subst + +# we use these a lot, so try to optimize them +is_String = SCons.Util.is_String +is_List = SCons.Util.is_List + +class _null(object): + pass + +print_actions = 1 +execute_actions = 1 +print_actions_presub = 0 + +def rfile(n): + try: + return n.rfile() + except AttributeError: + return n + +def default_exitstatfunc(s): + return s + +try: + SET_LINENO = dis.SET_LINENO + HAVE_ARGUMENT = dis.HAVE_ARGUMENT +except AttributeError: + remove_set_lineno_codes = lambda x: x +else: + def remove_set_lineno_codes(code): + result = [] + n = len(code) + i = 0 + while i < n: + c = code[i] + op = ord(c) + if op >= HAVE_ARGUMENT: + if op != SET_LINENO: + result.append(code[i:i+3]) + i = i+3 + else: + result.append(c) + i = i+1 + return ''.join(result) + +strip_quotes = re.compile('^[\'"](.*)[\'"]$') + + +def _callable_contents(obj): + """Return the signature contents of a callable Python object. + """ + try: + # Test if obj is a method. + return _function_contents(obj.im_func) + + except AttributeError: + try: + # Test if obj is a callable object. + return _function_contents(obj.__call__.im_func) + + except AttributeError: + try: + # Test if obj is a code object. + return _code_contents(obj) + + except AttributeError: + # Test if obj is a function object. + return _function_contents(obj) + + +def _object_contents(obj): + """Return the signature contents of any Python object. + + We have to handle the case where object contains a code object + since it can be pickled directly. + """ + try: + # Test if obj is a method. + return _function_contents(obj.im_func) + + except AttributeError: + try: + # Test if obj is a callable object. + return _function_contents(obj.__call__.im_func) + + except AttributeError: + try: + # Test if obj is a code object. + return _code_contents(obj) + + except AttributeError: + try: + # Test if obj is a function object. + return _function_contents(obj) + + except AttributeError: + # Should be a pickable Python object. + try: + return pickle.dumps(obj) + except (pickle.PicklingError, TypeError): + # This is weird, but it seems that nested classes + # are unpickable. The Python docs say it should + # always be a PicklingError, but some Python + # versions seem to return TypeError. Just do + # the best we can. + return str(obj) + + +def _code_contents(code): + """Return the signature contents of a code object. + + By providing direct access to the code object of the + function, Python makes this extremely easy. Hooray! + + Unfortunately, older versions of Python include line + number indications in the compiled byte code. Boo! + So we remove the line number byte codes to prevent + recompilations from moving a Python function. + """ + + contents = [] + + # The code contents depends on the number of local variables + # but not their actual names. + contents.append("%s,%s" % (code.co_argcount, len(code.co_varnames))) + try: + contents.append(",%s,%s" % (len(code.co_cellvars), len(code.co_freevars))) + except AttributeError: + # Older versions of Python do not support closures. + contents.append(",0,0") + + # The code contents depends on any constants accessed by the + # function. Note that we have to call _object_contents on each + # constants because the code object of nested functions can + # show-up among the constants. + # + # Note that we also always ignore the first entry of co_consts + # which contains the function doc string. We assume that the + # function does not access its doc string. + contents.append(',(' + ','.join(map(_object_contents,code.co_consts[1:])) + ')') + + # The code contents depends on the variable names used to + # accessed global variable, as changing the variable name changes + # the variable actually accessed and therefore changes the + # function result. + contents.append(',(' + ','.join(map(_object_contents,code.co_names)) + ')') + + + # The code contents depends on its actual code!!! + contents.append(',(' + str(remove_set_lineno_codes(code.co_code)) + ')') + + return ''.join(contents) + + +def _function_contents(func): + """Return the signature contents of a function.""" + + contents = [_code_contents(func.func_code)] + + # The function contents depends on the value of defaults arguments + if func.func_defaults: + contents.append(',(' + ','.join(map(_object_contents,func.func_defaults)) + ')') + else: + contents.append(',()') + + # The function contents depends on the closure captured cell values. + try: + closure = func.func_closure or [] + except AttributeError: + # Older versions of Python do not support closures. + closure = [] + + #xxx = [_object_contents(x.cell_contents) for x in closure] + try: + xxx = [_object_contents(x.cell_contents) for x in closure] + except AttributeError: + xxx = [] + contents.append(',(' + ','.join(xxx) + ')') + + return ''.join(contents) + + +def _actionAppend(act1, act2): + # This function knows how to slap two actions together. + # Mainly, it handles ListActions by concatenating into + # a single ListAction. + a1 = Action(act1) + a2 = Action(act2) + if a1 is None: + return a2 + if a2 is None: + return a1 + if isinstance(a1, ListAction): + if isinstance(a2, ListAction): + return ListAction(a1.list + a2.list) + else: + return ListAction(a1.list + [ a2 ]) + else: + if isinstance(a2, ListAction): + return ListAction([ a1 ] + a2.list) + else: + return ListAction([ a1, a2 ]) + +def _do_create_keywords(args, kw): + """This converts any arguments after the action argument into + their equivalent keywords and adds them to the kw argument. + """ + v = kw.get('varlist', ()) + # prevent varlist="FOO" from being interpreted as ['F', 'O', 'O'] + if is_String(v): v = (v,) + kw['varlist'] = tuple(v) + if args: + # turn positional args into equivalent keywords + cmdstrfunc = args[0] + if cmdstrfunc is None or is_String(cmdstrfunc): + kw['cmdstr'] = cmdstrfunc + elif callable(cmdstrfunc): + kw['strfunction'] = cmdstrfunc + else: + raise SCons.Errors.UserError( + 'Invalid command display variable type. ' + 'You must either pass a string or a callback which ' + 'accepts (target, source, env) as parameters.') + if len(args) > 1: + kw['varlist'] = args[1:] + kw['varlist'] + if kw.get('strfunction', _null) is not _null \ + and kw.get('cmdstr', _null) is not _null: + raise SCons.Errors.UserError( + 'Cannot have both strfunction and cmdstr args to Action()') + +def _do_create_action(act, kw): + """This is the actual "implementation" for the + Action factory method, below. This handles the + fact that passing lists to Action() itself has + different semantics than passing lists as elements + of lists. + + The former will create a ListAction, the latter + will create a CommandAction by converting the inner + list elements to strings.""" + + if isinstance(act, ActionBase): + return act + + if is_List(act): + return CommandAction(act, **kw) + + if callable(act): + try: + gen = kw['generator'] + del kw['generator'] + except KeyError: + gen = 0 + if gen: + action_type = CommandGeneratorAction + else: + action_type = FunctionAction + return action_type(act, kw) + + if is_String(act): + var=SCons.Util.get_environment_var(act) + if var: + # This looks like a string that is purely an Environment + # variable reference, like "$FOO" or "${FOO}". We do + # something special here...we lazily evaluate the contents + # of that Environment variable, so a user could put something + # like a function or a CommandGenerator in that variable + # instead of a string. + return LazyAction(var, kw) + commands = str(act).split('\n') + if len(commands) == 1: + return CommandAction(commands[0], **kw) + # The list of string commands may include a LazyAction, so we + # reprocess them via _do_create_list_action. + return _do_create_list_action(commands, kw) + # Catch a common error case with a nice message: + if isinstance(act, int) or isinstance(act, float): + raise TypeError("Don't know how to create an Action from a number (%s)"%act) + # Else fail silently (???) + return None + +def _do_create_list_action(act, kw): + """A factory for list actions. Convert the input list into Actions + and then wrap them in a ListAction.""" + acts = [] + for a in act: + aa = _do_create_action(a, kw) + if aa is not None: acts.append(aa) + if not acts: + return ListAction([]) + elif len(acts) == 1: + return acts[0] + else: + return ListAction(acts) + +def Action(act, *args, **kw): + """A factory for action objects.""" + # Really simple: the _do_create_* routines do the heavy lifting. + _do_create_keywords(args, kw) + if is_List(act): + return _do_create_list_action(act, kw) + return _do_create_action(act, kw) + +class ActionBase(object): + """Base class for all types of action objects that can be held by + other objects (Builders, Executors, etc.) This provides the + common methods for manipulating and combining those actions.""" + + def __cmp__(self, other): + return cmp(self.__dict__, other) + + def no_batch_key(self, env, target, source): + return None + + batch_key = no_batch_key + + def genstring(self, target, source, env): + return str(self) + + def get_contents(self, target, source, env): + result = [ self.get_presig(target, source, env) ] + # This should never happen, as the Action() factory should wrap + # the varlist, but just in case an action is created directly, + # we duplicate this check here. + vl = self.get_varlist(target, source, env) + if is_String(vl): vl = (vl,) + for v in vl: + result.append(env.subst('${'+v+'}')) + return ''.join(result) + + def __add__(self, other): + return _actionAppend(self, other) + + def __radd__(self, other): + return _actionAppend(other, self) + + def presub_lines(self, env): + # CommandGeneratorAction needs a real environment + # in order to return the proper string here, since + # it may call LazyAction, which looks up a key + # in that env. So we temporarily remember the env here, + # and CommandGeneratorAction will use this env + # when it calls its _generate method. + self.presub_env = env + lines = str(self).split('\n') + self.presub_env = None # don't need this any more + return lines + + def get_varlist(self, target, source, env, executor=None): + return self.varlist + + def get_targets(self, env, executor): + """ + Returns the type of targets ($TARGETS, $CHANGED_TARGETS) used + by this action. + """ + return self.targets + +class _ActionAction(ActionBase): + """Base class for actions that create output objects.""" + def __init__(self, cmdstr=_null, strfunction=_null, varlist=(), + presub=_null, chdir=None, exitstatfunc=None, + batch_key=None, targets='$TARGETS', + **kw): + self.cmdstr = cmdstr + if strfunction is not _null: + if strfunction is None: + self.cmdstr = None + else: + self.strfunction = strfunction + self.varlist = varlist + self.presub = presub + self.chdir = chdir + if not exitstatfunc: + exitstatfunc = default_exitstatfunc + self.exitstatfunc = exitstatfunc + + self.targets = targets + + if batch_key: + if not callable(batch_key): + # They have set batch_key, but not to their own + # callable. The default behavior here will batch + # *all* targets+sources using this action, separated + # for each construction environment. + def default_batch_key(self, env, target, source): + return (id(self), id(env)) + batch_key = default_batch_key + SCons.Util.AddMethod(self, batch_key, 'batch_key') + + def print_cmd_line(self, s, target, source, env): + # In python 3, and in some of our tests, sys.stdout is + # a String io object, and it takes unicode strings only + # In other cases it's a regular Python 2.x file object + # which takes strings (bytes), and if you pass those a + # unicode object they try to decode with 'ascii' codec + # which fails if the cmd line has any hi-bit-set chars. + # This code assumes s is a regular string, but should + # work if it's unicode too. + try: + sys.stdout.write(unicode(s + "\n")) + except UnicodeDecodeError: + sys.stdout.write(s + "\n") + + def __call__(self, target, source, env, + exitstatfunc=_null, + presub=_null, + show=_null, + execute=_null, + chdir=_null, + executor=None): + if not is_List(target): + target = [target] + if not is_List(source): + source = [source] + + if presub is _null: + presub = self.presub + if presub is _null: + presub = print_actions_presub + if exitstatfunc is _null: exitstatfunc = self.exitstatfunc + if show is _null: show = print_actions + if execute is _null: execute = execute_actions + if chdir is _null: chdir = self.chdir + save_cwd = None + if chdir: + save_cwd = os.getcwd() + try: + chdir = str(chdir.abspath) + except AttributeError: + if not is_String(chdir): + if executor: + chdir = str(executor.batches[0].targets[0].dir) + else: + chdir = str(target[0].dir) + if presub: + if executor: + target = executor.get_all_targets() + source = executor.get_all_sources() + t = ' and '.join(map(str, target)) + l = '\n '.join(self.presub_lines(env)) + out = u"Building %s with action:\n %s\n" % (t, l) + sys.stdout.write(out) + cmd = None + if show and self.strfunction: + if executor: + target = executor.get_all_targets() + source = executor.get_all_sources() + try: + cmd = self.strfunction(target, source, env, executor) + except TypeError: + cmd = self.strfunction(target, source, env) + if cmd: + if chdir: + cmd = ('os.chdir(%s)\n' % repr(chdir)) + cmd + try: + get = env.get + except AttributeError: + print_func = self.print_cmd_line + else: + print_func = get('PRINT_CMD_LINE_FUNC') + if not print_func: + print_func = self.print_cmd_line + print_func(cmd, target, source, env) + stat = 0 + if execute: + if chdir: + os.chdir(chdir) + try: + stat = self.execute(target, source, env, executor=executor) + if isinstance(stat, SCons.Errors.BuildError): + s = exitstatfunc(stat.status) + if s: + stat.status = s + else: + stat = s + else: + stat = exitstatfunc(stat) + finally: + if save_cwd: + os.chdir(save_cwd) + if cmd and save_cwd: + print_func('os.chdir(%s)' % repr(save_cwd), target, source, env) + + return stat + + +def _string_from_cmd_list(cmd_list): + """Takes a list of command line arguments and returns a pretty + representation for printing.""" + cl = [] + for arg in map(str, cmd_list): + if ' ' in arg or '\t' in arg: + arg = '"' + arg + '"' + cl.append(arg) + return ' '.join(cl) + +# A fiddlin' little function that has an 'import SCons.Environment' which +# can't be moved to the top level without creating an import loop. Since +# this import creates a local variable named 'SCons', it blocks access to +# the global variable, so we move it here to prevent complaints about local +# variables being used uninitialized. +default_ENV = None +def get_default_ENV(env): + global default_ENV + try: + return env['ENV'] + except KeyError: + if not default_ENV: + import SCons.Environment + # This is a hideously expensive way to get a default shell + # environment. What it really should do is run the platform + # setup to get the default ENV. Fortunately, it's incredibly + # rare for an Environment not to have a shell environment, so + # we're not going to worry about it overmuch. + default_ENV = SCons.Environment.Environment()['ENV'] + return default_ENV + +# This function is still in draft mode. We're going to need something like +# it in the long run as more and more places use subprocess, but I'm sure +# it'll have to be tweaked to get the full desired functionality. +# one special arg (so far?), 'error', to tell what to do with exceptions. +def _subproc(scons_env, cmd, error = 'ignore', **kw): + """Do common setup for a subprocess.Popen() call""" + # allow std{in,out,err} to be "'devnull'" + io = kw.get('stdin') + if is_String(io) and io == 'devnull': + kw['stdin'] = open(os.devnull) + io = kw.get('stdout') + if is_String(io) and io == 'devnull': + kw['stdout'] = open(os.devnull, 'w') + io = kw.get('stderr') + if is_String(io) and io == 'devnull': + kw['stderr'] = open(os.devnull, 'w') + + # Figure out what shell environment to use + ENV = kw.get('env', None) + if ENV is None: ENV = get_default_ENV(scons_env) + + # Ensure that the ENV values are all strings: + new_env = {} + for key, value in ENV.items(): + if is_List(value): + # If the value is a list, then we assume it is a path list, + # because that's a pretty common list-like value to stick + # in an environment variable: + value = SCons.Util.flatten_sequence(value) + new_env[key] = os.pathsep.join(map(str, value)) + else: + # It's either a string or something else. If it's a string, + # we still want to call str() because it might be a *Unicode* + # string, which makes subprocess.Popen() gag. If it isn't a + # string or a list, then we just coerce it to a string, which + # is the proper way to handle Dir and File instances and will + # produce something reasonable for just about everything else: + new_env[key] = str(value) + kw['env'] = new_env + + try: + return subprocess.Popen(cmd, **kw) + except EnvironmentError, e: + if error == 'raise': raise + # return a dummy Popen instance that only returns error + class dummyPopen(object): + def __init__(self, e): self.exception = e + def communicate(self): return ('','') + def wait(self): return -self.exception.errno + stdin = None + class f(object): + def read(self): return '' + def readline(self): return '' + stdout = stderr = f() + return dummyPopen(e) + +class CommandAction(_ActionAction): + """Class for command-execution actions.""" + def __init__(self, cmd, **kw): + # Cmd can actually be a list or a single item; if it's a + # single item it should be the command string to execute; if a + # list then it should be the words of the command string to + # execute. Only a single command should be executed by this + # object; lists of commands should be handled by embedding + # these objects in a ListAction object (which the Action() + # factory above does). cmd will be passed to + # Environment.subst_list() for substituting environment + # variables. + if __debug__: logInstanceCreation(self, 'Action.CommandAction') + + _ActionAction.__init__(self, **kw) + if is_List(cmd): + if list(filter(is_List, cmd)): + raise TypeError("CommandAction should be given only " \ + "a single command") + self.cmd_list = cmd + + def __str__(self): + if is_List(self.cmd_list): + return ' '.join(map(str, self.cmd_list)) + return str(self.cmd_list) + + def process(self, target, source, env, executor=None): + if executor: + result = env.subst_list(self.cmd_list, 0, executor=executor) + else: + result = env.subst_list(self.cmd_list, 0, target, source) + silent = None + ignore = None + while True: + try: c = result[0][0][0] + except IndexError: c = None + if c == '@': silent = 1 + elif c == '-': ignore = 1 + else: break + result[0][0] = result[0][0][1:] + try: + if not result[0][0]: + result[0] = result[0][1:] + except IndexError: + pass + return result, ignore, silent + + def strfunction(self, target, source, env, executor=None): + if self.cmdstr is None: + return None + if self.cmdstr is not _null: + from SCons.Subst import SUBST_RAW + if executor: + c = env.subst(self.cmdstr, SUBST_RAW, executor=executor) + else: + c = env.subst(self.cmdstr, SUBST_RAW, target, source) + if c: + return c + cmd_list, ignore, silent = self.process(target, source, env, executor) + if silent: + return '' + return _string_from_cmd_list(cmd_list[0]) + + def execute(self, target, source, env, executor=None): + """Execute a command action. + + This will handle lists of commands as well as individual commands, + because construction variable substitution may turn a single + "command" into a list. This means that this class can actually + handle lists of commands, even though that's not how we use it + externally. + """ + escape_list = SCons.Subst.escape_list + flatten_sequence = SCons.Util.flatten_sequence + + try: + shell = env['SHELL'] + except KeyError: + raise SCons.Errors.UserError('Missing SHELL construction variable.') + + try: + spawn = env['SPAWN'] + except KeyError: + raise SCons.Errors.UserError('Missing SPAWN construction variable.') + else: + if is_String(spawn): + spawn = env.subst(spawn, raw=1, conv=lambda x: x) + + escape = env.get('ESCAPE', lambda x: x) + + ENV = get_default_ENV(env) + + # Ensure that the ENV values are all strings: + for key, value in ENV.items(): + if not is_String(value): + if is_List(value): + # If the value is a list, then we assume it is a + # path list, because that's a pretty common list-like + # value to stick in an environment variable: + value = flatten_sequence(value) + ENV[key] = os.pathsep.join(map(str, value)) + else: + # If it isn't a string or a list, then we just coerce + # it to a string, which is the proper way to handle + # Dir and File instances and will produce something + # reasonable for just about everything else: + ENV[key] = str(value) + + if executor: + target = executor.get_all_targets() + source = executor.get_all_sources() + cmd_list, ignore, silent = self.process(target, list(map(rfile, source)), env, executor) + + # Use len() to filter out any "command" that's zero-length. + for cmd_line in filter(len, cmd_list): + # Escape the command line for the interpreter we are using. + cmd_line = escape_list(cmd_line, escape) + result = spawn(shell, escape, cmd_line[0], cmd_line, ENV) + if not ignore and result: + msg = "Error %s" % result + return SCons.Errors.BuildError(errstr=msg, + status=result, + action=self, + command=cmd_line) + return 0 + + def get_presig(self, target, source, env, executor=None): + """Return the signature contents of this action's command line. + + This strips $(-$) and everything in between the string, + since those parts don't affect signatures. + """ + from SCons.Subst import SUBST_SIG + cmd = self.cmd_list + if is_List(cmd): + cmd = ' '.join(map(str, cmd)) + else: + cmd = str(cmd) + if executor: + return env.subst_target_source(cmd, SUBST_SIG, executor=executor) + else: + return env.subst_target_source(cmd, SUBST_SIG, target, source) + + def get_implicit_deps(self, target, source, env, executor=None): + icd = env.get('IMPLICIT_COMMAND_DEPENDENCIES', True) + if is_String(icd) and icd[:1] == '$': + icd = env.subst(icd) + if not icd or icd in ('0', 'None'): + return [] + from SCons.Subst import SUBST_SIG + if executor: + cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, executor=executor) + else: + cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, target, source) + res = [] + for cmd_line in cmd_list: + if cmd_line: + d = str(cmd_line[0]) + m = strip_quotes.match(d) + if m: + d = m.group(1) + d = env.WhereIs(d) + if d: + res.append(env.fs.File(d)) + return res + +class CommandGeneratorAction(ActionBase): + """Class for command-generator actions.""" + def __init__(self, generator, kw): + if __debug__: logInstanceCreation(self, 'Action.CommandGeneratorAction') + self.generator = generator + self.gen_kw = kw + self.varlist = kw.get('varlist', ()) + self.targets = kw.get('targets', '$TARGETS') + + def _generate(self, target, source, env, for_signature, executor=None): + # ensure that target is a list, to make it easier to write + # generator functions: + if not is_List(target): + target = [target] + + if executor: + target = executor.get_all_targets() + source = executor.get_all_sources() + ret = self.generator(target=target, + source=source, + env=env, + for_signature=for_signature) + gen_cmd = Action(ret, **self.gen_kw) + if not gen_cmd: + raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret)) + return gen_cmd + + def __str__(self): + try: + env = self.presub_env + except AttributeError: + env = None + if env is None: + env = SCons.Defaults.DefaultEnvironment() + act = self._generate([], [], env, 1) + return str(act) + + def batch_key(self, env, target, source): + return self._generate(target, source, env, 1).batch_key(env, target, source) + + def genstring(self, target, source, env, executor=None): + return self._generate(target, source, env, 1, executor).genstring(target, source, env) + + def __call__(self, target, source, env, exitstatfunc=_null, presub=_null, + show=_null, execute=_null, chdir=_null, executor=None): + act = self._generate(target, source, env, 0, executor) + if act is None: + raise SCons.Errors.UserError("While building `%s': " + "Cannot deduce file extension from source files: %s" + % (repr(list(map(str, target))), repr(list(map(str, source))))) + return act(target, source, env, exitstatfunc, presub, + show, execute, chdir, executor) + + def get_presig(self, target, source, env, executor=None): + """Return the signature contents of this action's command line. + + This strips $(-$) and everything in between the string, + since those parts don't affect signatures. + """ + return self._generate(target, source, env, 1, executor).get_presig(target, source, env) + + def get_implicit_deps(self, target, source, env, executor=None): + return self._generate(target, source, env, 1, executor).get_implicit_deps(target, source, env) + + def get_varlist(self, target, source, env, executor=None): + return self._generate(target, source, env, 1, executor).get_varlist(target, source, env, executor) + + def get_targets(self, env, executor): + return self._generate(None, None, env, 1, executor).get_targets(env, executor) + + + +# A LazyAction is a kind of hybrid generator and command action for +# strings of the form "$VAR". These strings normally expand to other +# strings (think "$CCCOM" to "$CC -c -o $TARGET $SOURCE"), but we also +# want to be able to replace them with functions in the construction +# environment. Consequently, we want lazy evaluation and creation of +# an Action in the case of the function, but that's overkill in the more +# normal case of expansion to other strings. +# +# So we do this with a subclass that's both a generator *and* +# a command action. The overridden methods all do a quick check +# of the construction variable, and if it's a string we just call +# the corresponding CommandAction method to do the heavy lifting. +# If not, then we call the same-named CommandGeneratorAction method. +# The CommandGeneratorAction methods work by using the overridden +# _generate() method, that is, our own way of handling "generation" of +# an action based on what's in the construction variable. + +class LazyAction(CommandGeneratorAction, CommandAction): + + def __init__(self, var, kw): + if __debug__: logInstanceCreation(self, 'Action.LazyAction') + #FUTURE CommandAction.__init__(self, '${'+var+'}', **kw) + CommandAction.__init__(self, '${'+var+'}', **kw) + self.var = SCons.Util.to_String(var) + self.gen_kw = kw + + def get_parent_class(self, env): + c = env.get(self.var) + if is_String(c) and not '\n' in c: + return CommandAction + return CommandGeneratorAction + + def _generate_cache(self, env): + if env: + c = env.get(self.var, '') + else: + c = '' + gen_cmd = Action(c, **self.gen_kw) + if not gen_cmd: + raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c))) + return gen_cmd + + def _generate(self, target, source, env, for_signature, executor=None): + return self._generate_cache(env) + + def __call__(self, target, source, env, *args, **kw): + c = self.get_parent_class(env) + return c.__call__(self, target, source, env, *args, **kw) + + def get_presig(self, target, source, env): + c = self.get_parent_class(env) + return c.get_presig(self, target, source, env) + + def get_varlist(self, target, source, env, executor=None): + c = self.get_parent_class(env) + return c.get_varlist(self, target, source, env, executor) + + +class FunctionAction(_ActionAction): + """Class for Python function actions.""" + + def __init__(self, execfunction, kw): + if __debug__: logInstanceCreation(self, 'Action.FunctionAction') + + self.execfunction = execfunction + try: + self.funccontents = _callable_contents(execfunction) + except AttributeError: + try: + # See if execfunction will do the heavy lifting for us. + self.gc = execfunction.get_contents + except AttributeError: + # This is weird, just do the best we can. + self.funccontents = _object_contents(execfunction) + + _ActionAction.__init__(self, **kw) + + def function_name(self): + try: + return self.execfunction.__name__ + except AttributeError: + try: + return self.execfunction.__class__.__name__ + except AttributeError: + return "unknown_python_function" + + def strfunction(self, target, source, env, executor=None): + if self.cmdstr is None: + return None + if self.cmdstr is not _null: + from SCons.Subst import SUBST_RAW + if executor: + c = env.subst(self.cmdstr, SUBST_RAW, executor=executor) + else: + c = env.subst(self.cmdstr, SUBST_RAW, target, source) + if c: + return c + def array(a): + def quote(s): + try: + str_for_display = s.str_for_display + except AttributeError: + s = repr(s) + else: + s = str_for_display() + return s + return '[' + ", ".join(map(quote, a)) + ']' + try: + strfunc = self.execfunction.strfunction + except AttributeError: + pass + else: + if strfunc is None: + return None + if callable(strfunc): + return strfunc(target, source, env) + name = self.function_name() + tstr = array(target) + sstr = array(source) + return "%s(%s, %s)" % (name, tstr, sstr) + + def __str__(self): + name = self.function_name() + if name == 'ActionCaller': + return str(self.execfunction) + return "%s(target, source, env)" % name + + def execute(self, target, source, env, executor=None): + exc_info = (None,None,None) + try: + if executor: + target = executor.get_all_targets() + source = executor.get_all_sources() + rsources = list(map(rfile, source)) + try: + result = self.execfunction(target=target, source=rsources, env=env) + except KeyboardInterrupt, e: + raise + except SystemExit, e: + raise + except Exception, e: + result = e + exc_info = sys.exc_info() + + if result: + result = SCons.Errors.convert_to_BuildError(result, exc_info) + result.node=target + result.action=self + try: + result.command=self.strfunction(target, source, env, executor) + except TypeError: + result.command=self.strfunction(target, source, env) + + # FIXME: This maintains backward compatibility with respect to + # which type of exceptions were returned by raising an + # exception and which ones were returned by value. It would + # probably be best to always return them by value here, but + # some codes do not check the return value of Actions and I do + # not have the time to modify them at this point. + if (exc_info[1] and + not isinstance(exc_info[1],EnvironmentError)): + raise result + + return result + finally: + # Break the cycle between the traceback object and this + # function stack frame. See the sys.exc_info() doc info for + # more information about this issue. + del exc_info + + + def get_presig(self, target, source, env): + """Return the signature contents of this callable action.""" + try: + return self.gc(target, source, env) + except AttributeError: + return self.funccontents + + def get_implicit_deps(self, target, source, env): + return [] + +class ListAction(ActionBase): + """Class for lists of other actions.""" + def __init__(self, actionlist): + if __debug__: logInstanceCreation(self, 'Action.ListAction') + def list_of_actions(x): + if isinstance(x, ActionBase): + return x + return Action(x) + self.list = list(map(list_of_actions, actionlist)) + # our children will have had any varlist + # applied; we don't need to do it again + self.varlist = () + self.targets = '$TARGETS' + + def genstring(self, target, source, env): + return '\n'.join([a.genstring(target, source, env) for a in self.list]) + + def __str__(self): + return '\n'.join(map(str, self.list)) + + def presub_lines(self, env): + return SCons.Util.flatten_sequence( + [a.presub_lines(env) for a in self.list]) + + def get_presig(self, target, source, env): + """Return the signature contents of this action list. + + Simple concatenation of the signatures of the elements. + """ + return "".join([x.get_contents(target, source, env) for x in self.list]) + + def __call__(self, target, source, env, exitstatfunc=_null, presub=_null, + show=_null, execute=_null, chdir=_null, executor=None): + if executor: + target = executor.get_all_targets() + source = executor.get_all_sources() + for act in self.list: + stat = act(target, source, env, exitstatfunc, presub, + show, execute, chdir, executor) + if stat: + return stat + return 0 + + def get_implicit_deps(self, target, source, env): + result = [] + for act in self.list: + result.extend(act.get_implicit_deps(target, source, env)) + return result + + def get_varlist(self, target, source, env, executor=None): + result = SCons.Util.OrderedDict() + for act in self.list: + for var in act.get_varlist(target, source, env, executor): + result[var] = True + return list(result.keys()) + +class ActionCaller(object): + """A class for delaying calling an Action function with specific + (positional and keyword) arguments until the Action is actually + executed. + + This class looks to the rest of the world like a normal Action object, + but what it's really doing is hanging on to the arguments until we + have a target, source and env to use for the expansion. + """ + def __init__(self, parent, args, kw): + self.parent = parent + self.args = args + self.kw = kw + + def get_contents(self, target, source, env): + actfunc = self.parent.actfunc + try: + # "self.actfunc" is a function. + contents = str(actfunc.func_code.co_code) + except AttributeError: + # "self.actfunc" is a callable object. + try: + contents = str(actfunc.__call__.im_func.func_code.co_code) + except AttributeError: + # No __call__() method, so it might be a builtin + # or something like that. Do the best we can. + contents = str(actfunc) + contents = remove_set_lineno_codes(contents) + return contents + + def subst(self, s, target, source, env): + # If s is a list, recursively apply subst() + # to every element in the list + if is_List(s): + result = [] + for elem in s: + result.append(self.subst(elem, target, source, env)) + return self.parent.convert(result) + + # Special-case hack: Let a custom function wrapped in an + # ActionCaller get at the environment through which the action + # was called by using this hard-coded value as a special return. + if s == '$__env__': + return env + elif is_String(s): + return env.subst(s, 1, target, source) + return self.parent.convert(s) + + def subst_args(self, target, source, env): + return [self.subst(x, target, source, env) for x in self.args] + + def subst_kw(self, target, source, env): + kw = {} + for key in self.kw.keys(): + kw[key] = self.subst(self.kw[key], target, source, env) + return kw + + def __call__(self, target, source, env, executor=None): + args = self.subst_args(target, source, env) + kw = self.subst_kw(target, source, env) + return self.parent.actfunc(*args, **kw) + + def strfunction(self, target, source, env): + args = self.subst_args(target, source, env) + kw = self.subst_kw(target, source, env) + return self.parent.strfunc(*args, **kw) + + def __str__(self): + return self.parent.strfunc(*self.args, **self.kw) + +class ActionFactory(object): + """A factory class that will wrap up an arbitrary function + as an SCons-executable Action object. + + The real heavy lifting here is done by the ActionCaller class. + We just collect the (positional and keyword) arguments that we're + called with and give them to the ActionCaller object we create, + so it can hang onto them until it needs them. + """ + def __init__(self, actfunc, strfunc, convert=lambda x: x): + self.actfunc = actfunc + self.strfunc = strfunc + self.convert = convert + + def __call__(self, *args, **kw): + ac = ActionCaller(self, args, kw) + action = Action(ac, strfunction=ac.strfunction) + return action + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Builder.py b/scons/scons-local-2.3.0/SCons/Builder.py new file mode 100644 index 000000000..2c9958f85 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Builder.py @@ -0,0 +1,877 @@ +"""SCons.Builder + +Builder object subsystem. + +A Builder object is a callable that encapsulates information about how +to execute actions to create a target Node (file) from source Nodes +(files), and how to create those dependencies for tracking. + +The main entry point here is the Builder() factory method. This provides +a procedural interface that creates the right underlying Builder object +based on the keyword arguments supplied and the types of the arguments. + +The goal is for this external interface to be simple enough that the +vast majority of users can create new Builders as necessary to support +building new types of files in their configurations, without having to +dive any deeper into this subsystem. + +The base class here is BuilderBase. This is a concrete base class which +does, in fact, represent the Builder objects that we (or users) create. + +There is also a proxy that looks like a Builder: + + CompositeBuilder + + This proxies for a Builder with an action that is actually a + dictionary that knows how to map file suffixes to a specific + action. This is so that we can invoke different actions + (compilers, compile options) for different flavors of source + files. + +Builders and their proxies have the following public interface methods +used by other modules: + + __call__() + THE public interface. Calling a Builder object (with the + use of internal helper methods) sets up the target and source + dependencies, appropriate mapping to a specific action, and the + environment manipulation necessary for overridden construction + variable. This also takes care of warning about possible mistakes + in keyword arguments. + + add_emitter() + Adds an emitter for a specific file suffix, used by some Tool + modules to specify that (for example) a yacc invocation on a .y + can create a .h *and* a .c file. + + add_action() + Adds an action for a specific file suffix, heavily used by + Tool modules to add their specific action(s) for turning + a source file into an object file to the global static + and shared object file Builders. + +There are the following methods for internal use within this module: + + _execute() + The internal method that handles the heavily lifting when a + Builder is called. This is used so that the __call__() methods + can set up warning about possible mistakes in keyword-argument + overrides, and *then* execute all of the steps necessary so that + the warnings only occur once. + + get_name() + Returns the Builder's name within a specific Environment, + primarily used to try to return helpful information in error + messages. + + adjust_suffix() + get_prefix() + get_suffix() + get_src_suffix() + set_src_suffix() + Miscellaneous stuff for handling the prefix and suffix + manipulation we use in turning source file names into target + file names. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Builder.py 2013/03/03 09:48:35 garyo" + +import collections + +import SCons.Action +from SCons.Debug import logInstanceCreation +from SCons.Errors import InternalError, UserError +import SCons.Executor +import SCons.Memoize +import SCons.Node +import SCons.Node.FS +import SCons.Util +import SCons.Warnings + +class _Null(object): + pass + +_null = _Null + +def match_splitext(path, suffixes = []): + if suffixes: + matchsuf = [S for S in suffixes if path[-len(S):] == S] + if matchsuf: + suf = max([(len(_f),_f) for _f in matchsuf])[1] + return [path[:-len(suf)], path[-len(suf):]] + return SCons.Util.splitext(path) + +class DictCmdGenerator(SCons.Util.Selector): + """This is a callable class that can be used as a + command generator function. It holds on to a dictionary + mapping file suffixes to Actions. It uses that dictionary + to return the proper action based on the file suffix of + the source file.""" + + def __init__(self, dict=None, source_ext_match=1): + SCons.Util.Selector.__init__(self, dict) + self.source_ext_match = source_ext_match + + def src_suffixes(self): + return list(self.keys()) + + def add_action(self, suffix, action): + """Add a suffix-action pair to the mapping. + """ + self[suffix] = action + + def __call__(self, target, source, env, for_signature): + if not source: + return [] + + if self.source_ext_match: + suffixes = self.src_suffixes() + ext = None + for src in map(str, source): + my_ext = match_splitext(src, suffixes)[1] + if ext and my_ext != ext: + raise UserError("While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s" + % (repr(list(map(str, target))), src, ext, my_ext)) + ext = my_ext + else: + ext = match_splitext(str(source[0]), self.src_suffixes())[1] + + if not ext: + #return ext + raise UserError("While building `%s': " + "Cannot deduce file extension from source files: %s" + % (repr(list(map(str, target))), repr(list(map(str, source))))) + + try: + ret = SCons.Util.Selector.__call__(self, env, source, ext) + except KeyError, e: + raise UserError("Ambiguous suffixes after environment substitution: %s == %s == %s" % (e.args[0], e.args[1], e.args[2])) + if ret is None: + raise UserError("While building `%s' from `%s': Don't know how to build from a source file with suffix `%s'. Expected a suffix in this list: %s." % \ + (repr(list(map(str, target))), repr(list(map(str, source))), ext, repr(list(self.keys())))) + return ret + +class CallableSelector(SCons.Util.Selector): + """A callable dictionary that will, in turn, call the value it + finds if it can.""" + def __call__(self, env, source): + value = SCons.Util.Selector.__call__(self, env, source) + if callable(value): + value = value(env, source) + return value + +class DictEmitter(SCons.Util.Selector): + """A callable dictionary that maps file suffixes to emitters. + When called, it finds the right emitter in its dictionary for the + suffix of the first source file, and calls that emitter to get the + right lists of targets and sources to return. If there's no emitter + for the suffix in its dictionary, the original target and source are + returned. + """ + def __call__(self, target, source, env): + emitter = SCons.Util.Selector.__call__(self, env, source) + if emitter: + target, source = emitter(target, source, env) + return (target, source) + +class ListEmitter(collections.UserList): + """A callable list of emitters that calls each in sequence, + returning the result. + """ + def __call__(self, target, source, env): + for e in self.data: + target, source = e(target, source, env) + return (target, source) + +# These are a common errors when calling a Builder; +# they are similar to the 'target' and 'source' keyword args to builders, +# so we issue warnings when we see them. The warnings can, of course, +# be disabled. +misleading_keywords = { + 'targets' : 'target', + 'sources' : 'source', +} + +class OverrideWarner(collections.UserDict): + """A class for warning about keyword arguments that we use as + overrides in a Builder call. + + This class exists to handle the fact that a single Builder call + can actually invoke multiple builders. This class only emits the + warnings once, no matter how many Builders are invoked. + """ + def __init__(self, dict): + collections.UserDict.__init__(self, dict) + if __debug__: logInstanceCreation(self, 'Builder.OverrideWarner') + self.already_warned = None + def warn(self): + if self.already_warned: + return + for k in self.keys(): + if k in misleading_keywords: + alt = misleading_keywords[k] + msg = "Did you mean to use `%s' instead of `%s'?" % (alt, k) + SCons.Warnings.warn(SCons.Warnings.MisleadingKeywordsWarning, msg) + self.already_warned = 1 + +def Builder(**kw): + """A factory for builder objects.""" + composite = None + if 'generator' in kw: + if 'action' in kw: + raise UserError("You must not specify both an action and a generator.") + kw['action'] = SCons.Action.CommandGeneratorAction(kw['generator'], {}) + del kw['generator'] + elif 'action' in kw: + source_ext_match = kw.get('source_ext_match', 1) + if 'source_ext_match' in kw: + del kw['source_ext_match'] + if SCons.Util.is_Dict(kw['action']): + composite = DictCmdGenerator(kw['action'], source_ext_match) + kw['action'] = SCons.Action.CommandGeneratorAction(composite, {}) + kw['src_suffix'] = composite.src_suffixes() + else: + kw['action'] = SCons.Action.Action(kw['action']) + + if 'emitter' in kw: + emitter = kw['emitter'] + if SCons.Util.is_String(emitter): + # This allows users to pass in an Environment + # variable reference (like "$FOO") as an emitter. + # We will look in that Environment variable for + # a callable to use as the actual emitter. + var = SCons.Util.get_environment_var(emitter) + if not var: + raise UserError("Supplied emitter '%s' does not appear to refer to an Environment variable" % emitter) + kw['emitter'] = EmitterProxy(var) + elif SCons.Util.is_Dict(emitter): + kw['emitter'] = DictEmitter(emitter) + elif SCons.Util.is_List(emitter): + kw['emitter'] = ListEmitter(emitter) + + result = BuilderBase(**kw) + + if not composite is None: + result = CompositeBuilder(result, composite) + + return result + +def _node_errors(builder, env, tlist, slist): + """Validate that the lists of target and source nodes are + legal for this builder and environment. Raise errors or + issue warnings as appropriate. + """ + + # First, figure out if there are any errors in the way the targets + # were specified. + for t in tlist: + if t.side_effect: + raise UserError("Multiple ways to build the same target were specified for: %s" % t) + if t.has_explicit_builder(): + if not t.env is None and not t.env is env: + action = t.builder.action + t_contents = action.get_contents(tlist, slist, t.env) + contents = action.get_contents(tlist, slist, env) + + if t_contents == contents: + msg = "Two different environments were specified for target %s,\n\tbut they appear to have the same action: %s" % (t, action.genstring(tlist, slist, t.env)) + SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarning, msg) + else: + msg = "Two environments with different actions were specified for the same target: %s" % t + raise UserError(msg) + if builder.multi: + if t.builder != builder: + msg = "Two different builders (%s and %s) were specified for the same target: %s" % (t.builder.get_name(env), builder.get_name(env), t) + raise UserError(msg) + # TODO(batch): list constructed each time! + if t.get_executor().get_all_targets() != tlist: + msg = "Two different target lists have a target in common: %s (from %s and from %s)" % (t, list(map(str, t.get_executor().get_all_targets())), list(map(str, tlist))) + raise UserError(msg) + elif t.sources != slist: + msg = "Multiple ways to build the same target were specified for: %s (from %s and from %s)" % (t, list(map(str, t.sources)), list(map(str, slist))) + raise UserError(msg) + + if builder.single_source: + if len(slist) > 1: + raise UserError("More than one source given for single-source builder: targets=%s sources=%s" % (list(map(str,tlist)), list(map(str,slist)))) + +class EmitterProxy(object): + """This is a callable class that can act as a + Builder emitter. It holds on to a string that + is a key into an Environment dictionary, and will + look there at actual build time to see if it holds + a callable. If so, we will call that as the actual + emitter.""" + def __init__(self, var): + self.var = SCons.Util.to_String(var) + + def __call__(self, target, source, env): + emitter = self.var + + # Recursively substitute the variable. + # We can't use env.subst() because it deals only + # in strings. Maybe we should change that? + while SCons.Util.is_String(emitter) and emitter in env: + emitter = env[emitter] + if callable(emitter): + target, source = emitter(target, source, env) + elif SCons.Util.is_List(emitter): + for e in emitter: + target, source = e(target, source, env) + + return (target, source) + + + def __cmp__(self, other): + return cmp(self.var, other.var) + +class BuilderBase(object): + """Base class for Builders, objects that create output + nodes (files) from input nodes (files). + """ + + if SCons.Memoize.use_memoizer: + __metaclass__ = SCons.Memoize.Memoized_Metaclass + + memoizer_counters = [] + + def __init__(self, action = None, + prefix = '', + suffix = '', + src_suffix = '', + target_factory = None, + source_factory = None, + target_scanner = None, + source_scanner = None, + emitter = None, + multi = 0, + env = None, + single_source = 0, + name = None, + chdir = _null, + is_explicit = 1, + src_builder = None, + ensure_suffix = False, + **overrides): + if __debug__: logInstanceCreation(self, 'Builder.BuilderBase') + self._memo = {} + self.action = action + self.multi = multi + if SCons.Util.is_Dict(prefix): + prefix = CallableSelector(prefix) + self.prefix = prefix + if SCons.Util.is_Dict(suffix): + suffix = CallableSelector(suffix) + self.env = env + self.single_source = single_source + if 'overrides' in overrides: + SCons.Warnings.warn(SCons.Warnings.DeprecatedBuilderKeywordsWarning, + "The \"overrides\" keyword to Builder() creation has been deprecated;\n" +\ + "\tspecify the items as keyword arguments to the Builder() call instead.") + overrides.update(overrides['overrides']) + del overrides['overrides'] + if 'scanner' in overrides: + SCons.Warnings.warn(SCons.Warnings.DeprecatedBuilderKeywordsWarning, + "The \"scanner\" keyword to Builder() creation has been deprecated;\n" + "\tuse: source_scanner or target_scanner as appropriate.") + del overrides['scanner'] + self.overrides = overrides + + self.set_suffix(suffix) + self.set_src_suffix(src_suffix) + self.ensure_suffix = ensure_suffix + + self.target_factory = target_factory + self.source_factory = source_factory + self.target_scanner = target_scanner + self.source_scanner = source_scanner + + self.emitter = emitter + + # Optional Builder name should only be used for Builders + # that don't get attached to construction environments. + if name: + self.name = name + self.executor_kw = {} + if not chdir is _null: + self.executor_kw['chdir'] = chdir + self.is_explicit = is_explicit + + if src_builder is None: + src_builder = [] + elif not SCons.Util.is_List(src_builder): + src_builder = [ src_builder ] + self.src_builder = src_builder + + def __nonzero__(self): + raise InternalError("Do not test for the Node.builder attribute directly; use Node.has_builder() instead") + + def get_name(self, env): + """Attempts to get the name of the Builder. + + Look at the BUILDERS variable of env, expecting it to be a + dictionary containing this Builder, and return the key of the + dictionary. If there's no key, then return a directly-configured + name (if there is one) or the name of the class (by default).""" + + try: + index = list(env['BUILDERS'].values()).index(self) + return list(env['BUILDERS'].keys())[index] + except (AttributeError, KeyError, TypeError, ValueError): + try: + return self.name + except AttributeError: + return str(self.__class__) + + def __cmp__(self, other): + return cmp(self.__dict__, other.__dict__) + + def splitext(self, path, env=None): + if not env: + env = self.env + if env: + suffixes = self.src_suffixes(env) + else: + suffixes = [] + return match_splitext(path, suffixes) + + def _adjustixes(self, files, pre, suf, ensure_suffix=False): + if not files: + return [] + result = [] + if not SCons.Util.is_List(files): + files = [files] + + for f in files: + if SCons.Util.is_String(f): + f = SCons.Util.adjustixes(f, pre, suf, ensure_suffix) + result.append(f) + return result + + def _create_nodes(self, env, target = None, source = None): + """Create and return lists of target and source nodes. + """ + src_suf = self.get_src_suffix(env) + + target_factory = env.get_factory(self.target_factory) + source_factory = env.get_factory(self.source_factory) + + source = self._adjustixes(source, None, src_suf) + slist = env.arg2nodes(source, source_factory) + + pre = self.get_prefix(env, slist) + suf = self.get_suffix(env, slist) + + if target is None: + try: + t_from_s = slist[0].target_from_source + except AttributeError: + raise UserError("Do not know how to create a target from source `%s'" % slist[0]) + except IndexError: + tlist = [] + else: + splitext = lambda S: self.splitext(S,env) + tlist = [ t_from_s(pre, suf, splitext) ] + else: + target = self._adjustixes(target, pre, suf, self.ensure_suffix) + tlist = env.arg2nodes(target, target_factory, target=target, source=source) + + if self.emitter: + # The emitter is going to do str(node), but because we're + # being called *from* a builder invocation, the new targets + # don't yet have a builder set on them and will look like + # source files. Fool the emitter's str() calls by setting + # up a temporary builder on the new targets. + new_targets = [] + for t in tlist: + if not t.is_derived(): + t.builder_set(self) + new_targets.append(t) + + orig_tlist = tlist[:] + orig_slist = slist[:] + + target, source = self.emitter(target=tlist, source=slist, env=env) + + # Now delete the temporary builders that we attached to any + # new targets, so that _node_errors() doesn't do weird stuff + # to them because it thinks they already have builders. + for t in new_targets: + if t.builder is self: + # Only delete the temporary builder if the emitter + # didn't change it on us. + t.builder_set(None) + + # Have to call arg2nodes yet again, since it is legal for + # emitters to spit out strings as well as Node instances. + tlist = env.arg2nodes(target, target_factory, + target=orig_tlist, source=orig_slist) + slist = env.arg2nodes(source, source_factory, + target=orig_tlist, source=orig_slist) + + return tlist, slist + + def _execute(self, env, target, source, overwarn={}, executor_kw={}): + # We now assume that target and source are lists or None. + if self.src_builder: + source = self.src_builder_sources(env, source, overwarn) + + if self.single_source and len(source) > 1 and target is None: + result = [] + if target is None: target = [None]*len(source) + for tgt, src in zip(target, source): + if not tgt is None: tgt = [tgt] + if not src is None: src = [src] + result.extend(self._execute(env, tgt, src, overwarn)) + return SCons.Node.NodeList(result) + + overwarn.warn() + + tlist, slist = self._create_nodes(env, target, source) + + # Check for errors with the specified target/source lists. + _node_errors(self, env, tlist, slist) + + # The targets are fine, so find or make the appropriate Executor to + # build this particular list of targets from this particular list of + # sources. + + executor = None + key = None + + if self.multi: + try: + executor = tlist[0].get_executor(create = 0) + except (AttributeError, IndexError): + pass + else: + executor.add_sources(slist) + + if executor is None: + if not self.action: + fmt = "Builder %s must have an action to build %s." + raise UserError(fmt % (self.get_name(env or self.env), + list(map(str,tlist)))) + key = self.action.batch_key(env or self.env, tlist, slist) + if key: + try: + executor = SCons.Executor.GetBatchExecutor(key) + except KeyError: + pass + else: + executor.add_batch(tlist, slist) + + if executor is None: + executor = SCons.Executor.Executor(self.action, env, [], + tlist, slist, executor_kw) + if key: + SCons.Executor.AddBatchExecutor(key, executor) + + # Now set up the relevant information in the target Nodes themselves. + for t in tlist: + t.cwd = env.fs.getcwd() + t.builder_set(self) + t.env_set(env) + t.add_source(slist) + t.set_executor(executor) + t.set_explicit(self.is_explicit) + + return SCons.Node.NodeList(tlist) + + def __call__(self, env, target=None, source=None, chdir=_null, **kw): + # We now assume that target and source are lists or None. + # The caller (typically Environment.BuilderWrapper) is + # responsible for converting any scalar values to lists. + if chdir is _null: + ekw = self.executor_kw + else: + ekw = self.executor_kw.copy() + ekw['chdir'] = chdir + if kw: + if 'srcdir' in kw: + def prependDirIfRelative(f, srcdir=kw['srcdir']): + import os.path + if SCons.Util.is_String(f) and not os.path.isabs(f): + f = os.path.join(srcdir, f) + return f + if not SCons.Util.is_List(source): + source = [source] + source = list(map(prependDirIfRelative, source)) + del kw['srcdir'] + if self.overrides: + env_kw = self.overrides.copy() + env_kw.update(kw) + else: + env_kw = kw + else: + env_kw = self.overrides + env = env.Override(env_kw) + return self._execute(env, target, source, OverrideWarner(kw), ekw) + + def adjust_suffix(self, suff): + if suff and not suff[0] in [ '.', '_', '$' ]: + return '.' + suff + return suff + + def get_prefix(self, env, sources=[]): + prefix = self.prefix + if callable(prefix): + prefix = prefix(env, sources) + return env.subst(prefix) + + def set_suffix(self, suffix): + if not callable(suffix): + suffix = self.adjust_suffix(suffix) + self.suffix = suffix + + def get_suffix(self, env, sources=[]): + suffix = self.suffix + if callable(suffix): + suffix = suffix(env, sources) + return env.subst(suffix) + + def set_src_suffix(self, src_suffix): + if not src_suffix: + src_suffix = [] + elif not SCons.Util.is_List(src_suffix): + src_suffix = [ src_suffix ] + self.src_suffix = [callable(suf) and suf or self.adjust_suffix(suf) for suf in src_suffix] + + def get_src_suffix(self, env): + """Get the first src_suffix in the list of src_suffixes.""" + ret = self.src_suffixes(env) + if not ret: + return '' + return ret[0] + + def add_emitter(self, suffix, emitter): + """Add a suffix-emitter mapping to this Builder. + + This assumes that emitter has been initialized with an + appropriate dictionary type, and will throw a TypeError if + not, so the caller is responsible for knowing that this is an + appropriate method to call for the Builder in question. + """ + self.emitter[suffix] = emitter + + def add_src_builder(self, builder): + """ + Add a new Builder to the list of src_builders. + + This requires wiping out cached values so that the computed + lists of source suffixes get re-calculated. + """ + self._memo = {} + self.src_builder.append(builder) + + def _get_sdict(self, env): + """ + Returns a dictionary mapping all of the source suffixes of all + src_builders of this Builder to the underlying Builder that + should be called first. + + This dictionary is used for each target specified, so we save a + lot of extra computation by memoizing it for each construction + environment. + + Note that this is re-computed each time, not cached, because there + might be changes to one of our source Builders (or one of their + source Builders, and so on, and so on...) that we can't "see." + + The underlying methods we call cache their computed values, + though, so we hope repeatedly aggregating them into a dictionary + like this won't be too big a hit. We may need to look for a + better way to do this if performance data show this has turned + into a significant bottleneck. + """ + sdict = {} + for bld in self.get_src_builders(env): + for suf in bld.src_suffixes(env): + sdict[suf] = bld + return sdict + + def src_builder_sources(self, env, source, overwarn={}): + sdict = self._get_sdict(env) + + src_suffixes = self.src_suffixes(env) + + lengths = list(set(map(len, src_suffixes))) + + def match_src_suffix(name, src_suffixes=src_suffixes, lengths=lengths): + node_suffixes = [name[-l:] for l in lengths] + for suf in src_suffixes: + if suf in node_suffixes: + return suf + return None + + result = [] + for s in SCons.Util.flatten(source): + if SCons.Util.is_String(s): + match_suffix = match_src_suffix(env.subst(s)) + if not match_suffix and not '.' in s: + src_suf = self.get_src_suffix(env) + s = self._adjustixes(s, None, src_suf)[0] + else: + match_suffix = match_src_suffix(s.name) + if match_suffix: + try: + bld = sdict[match_suffix] + except KeyError: + result.append(s) + else: + tlist = bld._execute(env, None, [s], overwarn) + # If the subsidiary Builder returned more than one + # target, then filter out any sources that this + # Builder isn't capable of building. + if len(tlist) > 1: + tlist = [t for t in tlist if match_src_suffix(t.name)] + result.extend(tlist) + else: + result.append(s) + + source_factory = env.get_factory(self.source_factory) + + return env.arg2nodes(result, source_factory) + + def _get_src_builders_key(self, env): + return id(env) + + memoizer_counters.append(SCons.Memoize.CountDict('get_src_builders', _get_src_builders_key)) + + def get_src_builders(self, env): + """ + Returns the list of source Builders for this Builder. + + This exists mainly to look up Builders referenced as + strings in the 'BUILDER' variable of the construction + environment and cache the result. + """ + memo_key = id(env) + try: + memo_dict = self._memo['get_src_builders'] + except KeyError: + memo_dict = {} + self._memo['get_src_builders'] = memo_dict + else: + try: + return memo_dict[memo_key] + except KeyError: + pass + + builders = [] + for bld in self.src_builder: + if SCons.Util.is_String(bld): + try: + bld = env['BUILDERS'][bld] + except KeyError: + continue + builders.append(bld) + + memo_dict[memo_key] = builders + return builders + + def _subst_src_suffixes_key(self, env): + return id(env) + + memoizer_counters.append(SCons.Memoize.CountDict('subst_src_suffixes', _subst_src_suffixes_key)) + + def subst_src_suffixes(self, env): + """ + The suffix list may contain construction variable expansions, + so we have to evaluate the individual strings. To avoid doing + this over and over, we memoize the results for each construction + environment. + """ + memo_key = id(env) + try: + memo_dict = self._memo['subst_src_suffixes'] + except KeyError: + memo_dict = {} + self._memo['subst_src_suffixes'] = memo_dict + else: + try: + return memo_dict[memo_key] + except KeyError: + pass + suffixes = [env.subst(x) for x in self.src_suffix] + memo_dict[memo_key] = suffixes + return suffixes + + def src_suffixes(self, env): + """ + Returns the list of source suffixes for all src_builders of this + Builder. + + This is essentially a recursive descent of the src_builder "tree." + (This value isn't cached because there may be changes in a + src_builder many levels deep that we can't see.) + """ + sdict = {} + suffixes = self.subst_src_suffixes(env) + for s in suffixes: + sdict[s] = 1 + for builder in self.get_src_builders(env): + for s in builder.src_suffixes(env): + if s not in sdict: + sdict[s] = 1 + suffixes.append(s) + return suffixes + +class CompositeBuilder(SCons.Util.Proxy): + """A Builder Proxy whose main purpose is to always have + a DictCmdGenerator as its action, and to provide access + to the DictCmdGenerator's add_action() method. + """ + + def __init__(self, builder, cmdgen): + if __debug__: logInstanceCreation(self, 'Builder.CompositeBuilder') + SCons.Util.Proxy.__init__(self, builder) + + # cmdgen should always be an instance of DictCmdGenerator. + self.cmdgen = cmdgen + self.builder = builder + + __call__ = SCons.Util.Delegate('__call__') + + def add_action(self, suffix, action): + self.cmdgen.add_action(suffix, action) + self.set_src_suffix(self.cmdgen.src_suffixes()) + +def is_a_Builder(obj): + """"Returns True iff the specified obj is one of our Builder classes. + + The test is complicated a bit by the fact that CompositeBuilder + is a proxy, not a subclass of BuilderBase. + """ + return (isinstance(obj, BuilderBase) + or isinstance(obj, CompositeBuilder) + or callable(obj)) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/CacheDir.py b/scons/scons-local-2.3.0/SCons/CacheDir.py new file mode 100644 index 000000000..aef03b4b4 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/CacheDir.py @@ -0,0 +1,216 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/CacheDir.py 2013/03/03 09:48:35 garyo" + +__doc__ = """ +CacheDir support +""" + +import os.path +import stat +import sys + +import SCons.Action + +cache_enabled = True +cache_debug = False +cache_force = False +cache_show = False + +def CacheRetrieveFunc(target, source, env): + t = target[0] + fs = t.fs + cd = env.get_CacheDir() + cachedir, cachefile = cd.cachepath(t) + if not fs.exists(cachefile): + cd.CacheDebug('CacheRetrieve(%s): %s not in cache\n', t, cachefile) + return 1 + cd.CacheDebug('CacheRetrieve(%s): retrieving from %s\n', t, cachefile) + if SCons.Action.execute_actions: + if fs.islink(cachefile): + fs.symlink(fs.readlink(cachefile), t.path) + else: + env.copy_from_cache(cachefile, t.path) + st = fs.stat(cachefile) + fs.chmod(t.path, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) + return 0 + +def CacheRetrieveString(target, source, env): + t = target[0] + fs = t.fs + cd = env.get_CacheDir() + cachedir, cachefile = cd.cachepath(t) + if t.fs.exists(cachefile): + return "Retrieved `%s' from cache" % t.path + return None + +CacheRetrieve = SCons.Action.Action(CacheRetrieveFunc, CacheRetrieveString) + +CacheRetrieveSilent = SCons.Action.Action(CacheRetrieveFunc, None) + +def CachePushFunc(target, source, env): + t = target[0] + if t.nocache: + return + fs = t.fs + cd = env.get_CacheDir() + cachedir, cachefile = cd.cachepath(t) + if fs.exists(cachefile): + # Don't bother copying it if it's already there. Note that + # usually this "shouldn't happen" because if the file already + # existed in cache, we'd have retrieved the file from there, + # not built it. This can happen, though, in a race, if some + # other person running the same build pushes their copy to + # the cache after we decide we need to build it but before our + # build completes. + cd.CacheDebug('CachePush(%s): %s already exists in cache\n', t, cachefile) + return + + cd.CacheDebug('CachePush(%s): pushing to %s\n', t, cachefile) + + tempfile = cachefile+'.tmp'+str(os.getpid()) + errfmt = "Unable to copy %s to cache. Cache file is %s" + + if not fs.isdir(cachedir): + try: + fs.makedirs(cachedir) + except EnvironmentError: + # We may have received an exception because another process + # has beaten us creating the directory. + if not fs.isdir(cachedir): + msg = errfmt % (str(target), cachefile) + raise SCons.Errors.EnvironmentError(msg) + + try: + if fs.islink(t.path): + fs.symlink(fs.readlink(t.path), tempfile) + else: + fs.copy2(t.path, tempfile) + fs.rename(tempfile, cachefile) + st = fs.stat(t.path) + fs.chmod(cachefile, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) + except EnvironmentError: + # It's possible someone else tried writing the file at the + # same time we did, or else that there was some problem like + # the CacheDir being on a separate file system that's full. + # In any case, inability to push a file to cache doesn't affect + # the correctness of the build, so just print a warning. + msg = errfmt % (str(target), cachefile) + SCons.Warnings.warn(SCons.Warnings.CacheWriteErrorWarning, msg) + +CachePush = SCons.Action.Action(CachePushFunc, None) + +class CacheDir(object): + + def __init__(self, path): + try: + import hashlib + except ImportError: + msg = "No hashlib or MD5 module available, CacheDir() not supported" + SCons.Warnings.warn(SCons.Warnings.NoMD5ModuleWarning, msg) + self.path = None + else: + self.path = path + self.current_cache_debug = None + self.debugFP = None + + def CacheDebug(self, fmt, target, cachefile): + if cache_debug != self.current_cache_debug: + if cache_debug == '-': + self.debugFP = sys.stdout + elif cache_debug: + self.debugFP = open(cache_debug, 'w') + else: + self.debugFP = None + self.current_cache_debug = cache_debug + if self.debugFP: + self.debugFP.write(fmt % (target, os.path.split(cachefile)[1])) + + def is_enabled(self): + return (cache_enabled and not self.path is None) + + def cachepath(self, node): + """ + """ + if not self.is_enabled(): + return None, None + + sig = node.get_cachedir_bsig() + subdir = sig[0].upper() + dir = os.path.join(self.path, subdir) + return dir, os.path.join(dir, sig) + + def retrieve(self, node): + """ + This method is called from multiple threads in a parallel build, + so only do thread safe stuff here. Do thread unsafe stuff in + built(). + + Note that there's a special trick here with the execute flag + (one that's not normally done for other actions). Basically + if the user requested a no_exec (-n) build, then + SCons.Action.execute_actions is set to 0 and when any action + is called, it does its showing but then just returns zero + instead of actually calling the action execution operation. + The problem for caching is that if the file does NOT exist in + cache then the CacheRetrieveString won't return anything to + show for the task, but the Action.__call__ won't call + CacheRetrieveFunc; instead it just returns zero, which makes + the code below think that the file *was* successfully + retrieved from the cache, therefore it doesn't do any + subsequent building. However, the CacheRetrieveString didn't + print anything because it didn't actually exist in the cache, + and no more build actions will be performed, so the user just + sees nothing. The fix is to tell Action.__call__ to always + execute the CacheRetrieveFunc and then have the latter + explicitly check SCons.Action.execute_actions itself. + """ + if not self.is_enabled(): + return False + + env = node.get_build_env() + if cache_show: + if CacheRetrieveSilent(node, [], env, execute=1) == 0: + node.build(presub=0, execute=0) + return True + else: + if CacheRetrieve(node, [], env, execute=1) == 0: + return True + + return False + + def push(self, node): + if not self.is_enabled(): + return + return CachePush(node, [], node.get_build_env()) + + def push_if_forced(self, node): + if cache_force: + return self.push(node) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Conftest.py b/scons/scons-local-2.3.0/SCons/Conftest.py new file mode 100644 index 000000000..d4662780c --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Conftest.py @@ -0,0 +1,793 @@ +"""SCons.Conftest + +Autoconf-like configuration support; low level implementation of tests. +""" + +# +# Copyright (c) 2003 Stichting NLnet Labs +# Copyright (c) 2001, 2002, 2003 Steven Knight +# +# 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. +# + +# +# The purpose of this module is to define how a check is to be performed. +# Use one of the Check...() functions below. +# + +# +# A context class is used that defines functions for carrying out the tests, +# logging and messages. The following methods and members must be present: +# +# context.Display(msg) Function called to print messages that are normally +# displayed for the user. Newlines are explicitly used. +# The text should also be written to the logfile! +# +# context.Log(msg) Function called to write to a log file. +# +# context.BuildProg(text, ext) +# Function called to build a program, using "ext" for the +# file extention. Must return an empty string for +# success, an error message for failure. +# For reliable test results building should be done just +# like an actual program would be build, using the same +# command and arguments (including configure results so +# far). +# +# context.CompileProg(text, ext) +# Function called to compile a program, using "ext" for +# the file extention. Must return an empty string for +# success, an error message for failure. +# For reliable test results compiling should be done just +# like an actual source file would be compiled, using the +# same command and arguments (including configure results +# so far). +# +# context.AppendLIBS(lib_name_list) +# Append "lib_name_list" to the value of LIBS. +# "lib_namelist" is a list of strings. +# Return the value of LIBS before changing it (any type +# can be used, it is passed to SetLIBS() later.) +# +# context.PrependLIBS(lib_name_list) +# Prepend "lib_name_list" to the value of LIBS. +# "lib_namelist" is a list of strings. +# Return the value of LIBS before changing it (any type +# can be used, it is passed to SetLIBS() later.) +# +# context.SetLIBS(value) +# Set LIBS to "value". The type of "value" is what +# AppendLIBS() returned. +# Return the value of LIBS before changing it (any type +# can be used, it is passed to SetLIBS() later.) +# +# context.headerfilename +# Name of file to append configure results to, usually +# "confdefs.h". +# The file must not exist or be empty when starting. +# Empty or None to skip this (some tests will not work!). +# +# context.config_h (may be missing). If present, must be a string, which +# will be filled with the contents of a config_h file. +# +# context.vardict Dictionary holding variables used for the tests and +# stores results from the tests, used for the build +# commands. +# Normally contains "CC", "LIBS", "CPPFLAGS", etc. +# +# context.havedict Dictionary holding results from the tests that are to +# be used inside a program. +# Names often start with "HAVE_". These are zero +# (feature not present) or one (feature present). Other +# variables may have any value, e.g., "PERLVERSION" can +# be a number and "SYSTEMNAME" a string. +# + +import re +from types import IntType + +# +# PUBLIC VARIABLES +# + +LogInputFiles = 1 # Set that to log the input files in case of a failed test +LogErrorMessages = 1 # Set that to log Conftest-generated error messages + +# +# PUBLIC FUNCTIONS +# + +# Generic remarks: +# - When a language is specified which is not supported the test fails. The +# message is a bit different, because not all the arguments for the normal +# message are available yet (chicken-egg problem). + + +def CheckBuilder(context, text = None, language = None): + """ + Configure check to see if the compiler works. + Note that this uses the current value of compiler and linker flags, make + sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. + "language" should be "C" or "C++" and is used to select the compiler. + Default is "C". + "text" may be used to specify the code to be build. + Returns an empty string for success, an error message for failure. + """ + lang, suffix, msg = _lang2suffix(language) + if msg: + context.Display("%s\n" % msg) + return msg + + if not text: + text = """ +int main() { + return 0; +} +""" + + context.Display("Checking if building a %s file works... " % lang) + ret = context.BuildProg(text, suffix) + _YesNoResult(context, ret, None, text) + return ret + +def CheckCC(context): + """ + Configure check for a working C compiler. + + This checks whether the C compiler, as defined in the $CC construction + variable, can compile a C source file. It uses the current $CCCOM value + too, so that it can test against non working flags. + + """ + context.Display("Checking whether the C compiler works") + text = """ +int main() +{ + return 0; +} +""" + ret = _check_empty_program(context, 'CC', text, 'C') + _YesNoResult(context, ret, None, text) + return ret + +def CheckSHCC(context): + """ + Configure check for a working shared C compiler. + + This checks whether the C compiler, as defined in the $SHCC construction + variable, can compile a C source file. It uses the current $SHCCCOM value + too, so that it can test against non working flags. + + """ + context.Display("Checking whether the (shared) C compiler works") + text = """ +int foo() +{ + return 0; +} +""" + ret = _check_empty_program(context, 'SHCC', text, 'C', use_shared = True) + _YesNoResult(context, ret, None, text) + return ret + +def CheckCXX(context): + """ + Configure check for a working CXX compiler. + + This checks whether the CXX compiler, as defined in the $CXX construction + variable, can compile a CXX source file. It uses the current $CXXCOM value + too, so that it can test against non working flags. + + """ + context.Display("Checking whether the C++ compiler works") + text = """ +int main() +{ + return 0; +} +""" + ret = _check_empty_program(context, 'CXX', text, 'C++') + _YesNoResult(context, ret, None, text) + return ret + +def CheckSHCXX(context): + """ + Configure check for a working shared CXX compiler. + + This checks whether the CXX compiler, as defined in the $SHCXX construction + variable, can compile a CXX source file. It uses the current $SHCXXCOM value + too, so that it can test against non working flags. + + """ + context.Display("Checking whether the (shared) C++ compiler works") + text = """ +int main() +{ + return 0; +} +""" + ret = _check_empty_program(context, 'SHCXX', text, 'C++', use_shared = True) + _YesNoResult(context, ret, None, text) + return ret + +def _check_empty_program(context, comp, text, language, use_shared = False): + """Return 0 on success, 1 otherwise.""" + if comp not in context.env or not context.env[comp]: + # The compiler construction variable is not set or empty + return 1 + + lang, suffix, msg = _lang2suffix(language) + if msg: + return 1 + + if use_shared: + return context.CompileSharedObject(text, suffix) + else: + return context.CompileProg(text, suffix) + + +def CheckFunc(context, function_name, header = None, language = None): + """ + Configure check for a function "function_name". + "language" should be "C" or "C++" and is used to select the compiler. + Default is "C". + Optional "header" can be defined to define a function prototype, include a + header file or anything else that comes before main(). + Sets HAVE_function_name in context.havedict according to the result. + Note that this uses the current value of compiler and linker flags, make + sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. + Returns an empty string for success, an error message for failure. + """ + + # Remarks from autoconf: + # - Don't include because on OSF/1 3.0 it includes + # which includes which contains a prototype for select. + # Similarly for bzero. + # - assert.h is included to define __stub macros and hopefully few + # prototypes, which can conflict with char $1(); below. + # - Override any gcc2 internal prototype to avoid an error. + # - We use char for the function declaration because int might match the + # return type of a gcc2 builtin and then its argument prototype would + # still apply. + # - The GNU C library defines this for functions which it implements to + # always fail with ENOSYS. Some functions are actually named something + # starting with __ and the normal name is an alias. + + if context.headerfilename: + includetext = '#include "%s"' % context.headerfilename + else: + includetext = '' + if not header: + header = """ +#ifdef __cplusplus +extern "C" +#endif +char %s();""" % function_name + + lang, suffix, msg = _lang2suffix(language) + if msg: + context.Display("Cannot check for %s(): %s\n" % (function_name, msg)) + return msg + + text = """ +%(include)s +#include +%(hdr)s + +int main() { +#if defined (__stub_%(name)s) || defined (__stub___%(name)s) + fail fail fail +#else + %(name)s(); +#endif + + return 0; +} +""" % { 'name': function_name, + 'include': includetext, + 'hdr': header } + + context.Display("Checking for %s function %s()... " % (lang, function_name)) + ret = context.BuildProg(text, suffix) + _YesNoResult(context, ret, "HAVE_" + function_name, text, + "Define to 1 if the system has the function `%s'." %\ + function_name) + return ret + + +def CheckHeader(context, header_name, header = None, language = None, + include_quotes = None): + """ + Configure check for a C or C++ header file "header_name". + Optional "header" can be defined to do something before including the + header file (unusual, supported for consistency). + "language" should be "C" or "C++" and is used to select the compiler. + Default is "C". + Sets HAVE_header_name in context.havedict according to the result. + Note that this uses the current value of compiler and linker flags, make + sure $CFLAGS and $CPPFLAGS are set correctly. + Returns an empty string for success, an error message for failure. + """ + # Why compile the program instead of just running the preprocessor? + # It is possible that the header file exists, but actually using it may + # fail (e.g., because it depends on other header files). Thus this test is + # more strict. It may require using the "header" argument. + # + # Use <> by default, because the check is normally used for system header + # files. SCons passes '""' to overrule this. + + # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. + if context.headerfilename: + includetext = '#include "%s"\n' % context.headerfilename + else: + includetext = '' + if not header: + header = "" + + lang, suffix, msg = _lang2suffix(language) + if msg: + context.Display("Cannot check for header file %s: %s\n" + % (header_name, msg)) + return msg + + if not include_quotes: + include_quotes = "<>" + + text = "%s%s\n#include %s%s%s\n\n" % (includetext, header, + include_quotes[0], header_name, include_quotes[1]) + + context.Display("Checking for %s header file %s... " % (lang, header_name)) + ret = context.CompileProg(text, suffix) + _YesNoResult(context, ret, "HAVE_" + header_name, text, + "Define to 1 if you have the <%s> header file." % header_name) + return ret + + +def CheckType(context, type_name, fallback = None, + header = None, language = None): + """ + Configure check for a C or C++ type "type_name". + Optional "header" can be defined to include a header file. + "language" should be "C" or "C++" and is used to select the compiler. + Default is "C". + Sets HAVE_type_name in context.havedict according to the result. + Note that this uses the current value of compiler and linker flags, make + sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. + Returns an empty string for success, an error message for failure. + """ + + # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. + if context.headerfilename: + includetext = '#include "%s"' % context.headerfilename + else: + includetext = '' + if not header: + header = "" + + lang, suffix, msg = _lang2suffix(language) + if msg: + context.Display("Cannot check for %s type: %s\n" % (type_name, msg)) + return msg + + # Remarks from autoconf about this test: + # - Grepping for the type in include files is not reliable (grep isn't + # portable anyway). + # - Using "TYPE my_var;" doesn't work for const qualified types in C++. + # Adding an initializer is not valid for some C++ classes. + # - Using the type as parameter to a function either fails for K&$ C or for + # C++. + # - Using "TYPE *my_var;" is valid in C for some types that are not + # declared (struct something). + # - Using "sizeof(TYPE)" is valid when TYPE is actually a variable. + # - Using the previous two together works reliably. + text = """ +%(include)s +%(header)s + +int main() { + if ((%(name)s *) 0) + return 0; + if (sizeof (%(name)s)) + return 0; +} +""" % { 'include': includetext, + 'header': header, + 'name': type_name } + + context.Display("Checking for %s type %s... " % (lang, type_name)) + ret = context.BuildProg(text, suffix) + _YesNoResult(context, ret, "HAVE_" + type_name, text, + "Define to 1 if the system has the type `%s'." % type_name) + if ret and fallback and context.headerfilename: + f = open(context.headerfilename, "a") + f.write("typedef %s %s;\n" % (fallback, type_name)) + f.close() + + return ret + +def CheckTypeSize(context, type_name, header = None, language = None, expect = None): + """This check can be used to get the size of a given type, or to check whether + the type is of expected size. + + Arguments: + - type : str + the type to check + - includes : sequence + list of headers to include in the test code before testing the type + - language : str + 'C' or 'C++' + - expect : int + if given, will test wether the type has the given number of bytes. + If not given, will automatically find the size. + + Returns: + status : int + 0 if the check failed, or the found size of the type if the check succeeded.""" + + # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. + if context.headerfilename: + includetext = '#include "%s"' % context.headerfilename + else: + includetext = '' + + if not header: + header = "" + + lang, suffix, msg = _lang2suffix(language) + if msg: + context.Display("Cannot check for %s type: %s\n" % (type_name, msg)) + return msg + + src = includetext + header + if not expect is None: + # Only check if the given size is the right one + context.Display('Checking %s is %d bytes... ' % (type_name, expect)) + + # test code taken from autoconf: this is a pretty clever hack to find that + # a type is of a given size using only compilation. This speeds things up + # quite a bit compared to straightforward code using TryRun + src = src + r""" +typedef %s scons_check_type; + +int main() +{ + static int test_array[1 - 2 * !(((long int) (sizeof(scons_check_type))) == %d)]; + test_array[0] = 0; + + return 0; +} +""" + + st = context.CompileProg(src % (type_name, expect), suffix) + if not st: + context.Display("yes\n") + _Have(context, "SIZEOF_%s" % type_name, expect, + "The size of `%s', as computed by sizeof." % type_name) + return expect + else: + context.Display("no\n") + _LogFailed(context, src, st) + return 0 + else: + # Only check if the given size is the right one + context.Message('Checking size of %s ... ' % type_name) + + # We have to be careful with the program we wish to test here since + # compilation will be attempted using the current environment's flags. + # So make sure that the program will compile without any warning. For + # example using: 'int main(int argc, char** argv)' will fail with the + # '-Wall -Werror' flags since the variables argc and argv would not be + # used in the program... + # + src = src + """ +#include +#include +int main() { + printf("%d", (int)sizeof(""" + type_name + """)); + return 0; +} + """ + st, out = context.RunProg(src, suffix) + try: + size = int(out) + except ValueError: + # If cannot convert output of test prog to an integer (the size), + # something went wront, so just fail + st = 1 + size = 0 + + if not st: + context.Display("yes\n") + _Have(context, "SIZEOF_%s" % type_name, size, + "The size of `%s', as computed by sizeof." % type_name) + return size + else: + context.Display("no\n") + _LogFailed(context, src, st) + return 0 + + return 0 + +def CheckDeclaration(context, symbol, includes = None, language = None): + """Checks whether symbol is declared. + + Use the same test as autoconf, that is test whether the symbol is defined + as a macro or can be used as an r-value. + + Arguments: + symbol : str + the symbol to check + includes : str + Optional "header" can be defined to include a header file. + language : str + only C and C++ supported. + + Returns: + status : bool + True if the check failed, False if succeeded.""" + + # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. + if context.headerfilename: + includetext = '#include "%s"' % context.headerfilename + else: + includetext = '' + + if not includes: + includes = "" + + lang, suffix, msg = _lang2suffix(language) + if msg: + context.Display("Cannot check for declaration %s: %s\n" % (symbol, msg)) + return msg + + src = includetext + includes + context.Display('Checking whether %s is declared... ' % symbol) + + src = src + r""" +int main() +{ +#ifndef %s + (void) %s; +#endif + ; + return 0; +} +""" % (symbol, symbol) + + st = context.CompileProg(src, suffix) + _YesNoResult(context, st, "HAVE_DECL_" + symbol, src, + "Set to 1 if %s is defined." % symbol) + return st + +def CheckLib(context, libs, func_name = None, header = None, + extra_libs = None, call = None, language = None, autoadd = 1, + append = True): + """ + Configure check for a C or C++ libraries "libs". Searches through + the list of libraries, until one is found where the test succeeds. + Tests if "func_name" or "call" exists in the library. Note: if it exists + in another library the test succeeds anyway! + Optional "header" can be defined to include a header file. If not given a + default prototype for "func_name" is added. + Optional "extra_libs" is a list of library names to be added after + "lib_name" in the build command. To be used for libraries that "lib_name" + depends on. + Optional "call" replaces the call to "func_name" in the test code. It must + consist of complete C statements, including a trailing ";". + Both "func_name" and "call" arguments are optional, and in that case, just + linking against the libs is tested. + "language" should be "C" or "C++" and is used to select the compiler. + Default is "C". + Note that this uses the current value of compiler and linker flags, make + sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly. + Returns an empty string for success, an error message for failure. + """ + # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. + if context.headerfilename: + includetext = '#include "%s"' % context.headerfilename + else: + includetext = '' + if not header: + header = "" + + text = """ +%s +%s""" % (includetext, header) + + # Add a function declaration if needed. + if func_name and func_name != "main": + if not header: + text = text + """ +#ifdef __cplusplus +extern "C" +#endif +char %s(); +""" % func_name + + # The actual test code. + if not call: + call = "%s();" % func_name + + # if no function to test, leave main() blank + text = text + """ +int +main() { + %s +return 0; +} +""" % (call or "") + + if call: + i = call.find("\n") + if i > 0: + calltext = call[:i] + ".." + elif call[-1] == ';': + calltext = call[:-1] + else: + calltext = call + + for lib_name in libs: + + lang, suffix, msg = _lang2suffix(language) + if msg: + context.Display("Cannot check for library %s: %s\n" % (lib_name, msg)) + return msg + + # if a function was specified to run in main(), say it + if call: + context.Display("Checking for %s in %s library %s... " + % (calltext, lang, lib_name)) + # otherwise, just say the name of library and language + else: + context.Display("Checking for %s library %s... " + % (lang, lib_name)) + + if lib_name: + l = [ lib_name ] + if extra_libs: + l.extend(extra_libs) + if append: + oldLIBS = context.AppendLIBS(l) + else: + oldLIBS = context.PrependLIBS(l) + sym = "HAVE_LIB" + lib_name + else: + oldLIBS = -1 + sym = None + + ret = context.BuildProg(text, suffix) + + _YesNoResult(context, ret, sym, text, + "Define to 1 if you have the `%s' library." % lib_name) + if oldLIBS != -1 and (ret or not autoadd): + context.SetLIBS(oldLIBS) + + if not ret: + return ret + + return ret + +# +# END OF PUBLIC FUNCTIONS +# + +def _YesNoResult(context, ret, key, text, comment = None): + """ + Handle the result of a test with a "yes" or "no" result. + "ret" is the return value: empty if OK, error message when not. + "key" is the name of the symbol to be defined (HAVE_foo). + "text" is the source code of the program used for testing. + "comment" is the C comment to add above the line defining the symbol (the + comment is automatically put inside a /* */). If None, no comment is added. + """ + if key: + _Have(context, key, not ret, comment) + if ret: + context.Display("no\n") + _LogFailed(context, text, ret) + else: + context.Display("yes\n") + + +def _Have(context, key, have, comment = None): + """ + Store result of a test in context.havedict and context.headerfilename. + "key" is a "HAVE_abc" name. It is turned into all CAPITALS and non- + alphanumerics are replaced by an underscore. + The value of "have" can be: + 1 - Feature is defined, add "#define key". + 0 - Feature is not defined, add "/* #undef key */". + Adding "undef" is what autoconf does. Not useful for the + compiler, but it shows that the test was done. + number - Feature is defined to this number "#define key have". + Doesn't work for 0 or 1, use a string then. + string - Feature is defined to this string "#define key have". + Give "have" as is should appear in the header file, include quotes + when desired and escape special characters! + """ + key_up = key.upper() + key_up = re.sub('[^A-Z0-9_]', '_', key_up) + context.havedict[key_up] = have + if have == 1: + line = "#define %s 1\n" % key_up + elif have == 0: + line = "/* #undef %s */\n" % key_up + elif isinstance(have, IntType): + line = "#define %s %d\n" % (key_up, have) + else: + line = "#define %s %s\n" % (key_up, str(have)) + + if comment is not None: + lines = "\n/* %s */\n" % comment + line + else: + lines = "\n" + line + + if context.headerfilename: + f = open(context.headerfilename, "a") + f.write(lines) + f.close() + elif hasattr(context,'config_h'): + context.config_h = context.config_h + lines + + +def _LogFailed(context, text, msg): + """ + Write to the log about a failed program. + Add line numbers, so that error messages can be understood. + """ + if LogInputFiles: + context.Log("Failed program was:\n") + lines = text.split('\n') + if len(lines) and lines[-1] == '': + lines = lines[:-1] # remove trailing empty line + n = 1 + for line in lines: + context.Log("%d: %s\n" % (n, line)) + n = n + 1 + if LogErrorMessages: + context.Log("Error message: %s\n" % msg) + + +def _lang2suffix(lang): + """ + Convert a language name to a suffix. + When "lang" is empty or None C is assumed. + Returns a tuple (lang, suffix, None) when it works. + For an unrecognized language returns (None, None, msg). + Where: + lang = the unified language name + suffix = the suffix, including the leading dot + msg = an error message + """ + if not lang or lang in ["C", "c"]: + return ("C", ".c", None) + if lang in ["c++", "C++", "cpp", "CXX", "cxx"]: + return ("C++", ".cpp", None) + + return None, None, "Unsupported language: %s" % lang + + +# vim: set sw=4 et sts=4 tw=79 fo+=l: + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Debug.py b/scons/scons-local-2.3.0/SCons/Debug.py new file mode 100644 index 000000000..41b0ab6ee --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Debug.py @@ -0,0 +1,220 @@ +"""SCons.Debug + +Code for debugging SCons internal things. Shouldn't be +needed by most users. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Debug.py 2013/03/03 09:48:35 garyo" + +import os +import sys +import time +import weakref + +tracked_classes = {} + +def logInstanceCreation(instance, name=None): + if name is None: + name = instance.__class__.__name__ + if name not in tracked_classes: + tracked_classes[name] = [] + tracked_classes[name].append(weakref.ref(instance)) + +def string_to_classes(s): + if s == '*': + return sorted(tracked_classes.keys()) + else: + return s.split() + +def fetchLoggedInstances(classes="*"): + classnames = string_to_classes(classes) + return [(cn, len(tracked_classes[cn])) for cn in classnames] + +def countLoggedInstances(classes, file=sys.stdout): + for classname in string_to_classes(classes): + file.write("%s: %d\n" % (classname, len(tracked_classes[classname]))) + +def listLoggedInstances(classes, file=sys.stdout): + for classname in string_to_classes(classes): + file.write('\n%s:\n' % classname) + for ref in tracked_classes[classname]: + obj = ref() + if obj is not None: + file.write(' %s\n' % repr(obj)) + +def dumpLoggedInstances(classes, file=sys.stdout): + for classname in string_to_classes(classes): + file.write('\n%s:\n' % classname) + for ref in tracked_classes[classname]: + obj = ref() + if obj is not None: + file.write(' %s:\n' % obj) + for key, value in obj.__dict__.items(): + file.write(' %20s : %s\n' % (key, value)) + + + +if sys.platform[:5] == "linux": + # Linux doesn't actually support memory usage stats from getrusage(). + def memory(): + mstr = open('/proc/self/stat').read() + mstr = mstr.split()[22] + return int(mstr) +elif sys.platform[:6] == 'darwin': + #TODO really get memory stats for OS X + def memory(): + return 0 +else: + try: + import resource + except ImportError: + try: + import win32process + import win32api + except ImportError: + def memory(): + return 0 + else: + def memory(): + process_handle = win32api.GetCurrentProcess() + memory_info = win32process.GetProcessMemoryInfo( process_handle ) + return memory_info['PeakWorkingSetSize'] + else: + def memory(): + res = resource.getrusage(resource.RUSAGE_SELF) + return res[4] + +# returns caller's stack +def caller_stack(*backlist): + import traceback + if not backlist: + backlist = [0] + result = [] + for back in backlist: + tb = traceback.extract_stack(limit=3+back) + key = tb[0][:3] + result.append('%s:%d(%s)' % func_shorten(key)) + return result + +caller_bases = {} +caller_dicts = {} + +# trace a caller's stack +def caller_trace(back=0): + import traceback + tb = traceback.extract_stack(limit=3+back) + tb.reverse() + callee = tb[1][:3] + caller_bases[callee] = caller_bases.get(callee, 0) + 1 + for caller in tb[2:]: + caller = callee + caller[:3] + try: + entry = caller_dicts[callee] + except KeyError: + caller_dicts[callee] = entry = {} + entry[caller] = entry.get(caller, 0) + 1 + callee = caller + +# print a single caller and its callers, if any +def _dump_one_caller(key, file, level=0): + leader = ' '*level + for v,c in sorted([(-v,c) for c,v in caller_dicts[key].items()]): + file.write("%s %6d %s:%d(%s)\n" % ((leader,-v) + func_shorten(c[-3:]))) + if c in caller_dicts: + _dump_one_caller(c, file, level+1) + +# print each call tree +def dump_caller_counts(file=sys.stdout): + for k in sorted(caller_bases.keys()): + file.write("Callers of %s:%d(%s), %d calls:\n" + % (func_shorten(k) + (caller_bases[k],))) + _dump_one_caller(k, file) + +shorten_list = [ + ( '/scons/SCons/', 1), + ( '/src/engine/SCons/', 1), + ( '/usr/lib/python', 0), +] + +if os.sep != '/': + shorten_list = [(t[0].replace('/', os.sep), t[1]) for t in shorten_list] + +def func_shorten(func_tuple): + f = func_tuple[0] + for t in shorten_list: + i = f.find(t[0]) + if i >= 0: + if t[1]: + i = i + len(t[0]) + return (f[i:],)+func_tuple[1:] + return func_tuple + + +TraceFP = {} +if sys.platform == 'win32': + TraceDefault = 'con' +else: + TraceDefault = '/dev/tty' + +TimeStampDefault = None +StartTime = time.time() +PreviousTime = StartTime + +def Trace(msg, file=None, mode='w', tstamp=None): + """Write a trace message to a file. Whenever a file is specified, + it becomes the default for the next call to Trace().""" + global TraceDefault + global TimeStampDefault + global PreviousTime + if file is None: + file = TraceDefault + else: + TraceDefault = file + if tstamp is None: + tstamp = TimeStampDefault + else: + TimeStampDefault = tstamp + try: + fp = TraceFP[file] + except KeyError: + try: + fp = TraceFP[file] = open(file, mode) + except TypeError: + # Assume we were passed an open file pointer. + fp = file + if tstamp: + now = time.time() + fp.write('%8.4f %8.4f: ' % (now - StartTime, now - PreviousTime)) + PreviousTime = now + fp.write(msg) + fp.flush() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Defaults.py b/scons/scons-local-2.3.0/SCons/Defaults.py new file mode 100644 index 000000000..219190a9e --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Defaults.py @@ -0,0 +1,494 @@ +"""SCons.Defaults + +Builders and other things for the local site. Here's where we'll +duplicate the functionality of autoconf until we move it into the +installation procedure or use something like qmconf. + +The code that reads the registry to find MSVC components was borrowed +from distutils.msvccompiler. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# +from __future__ import division + +__revision__ = "src/engine/SCons/Defaults.py 2013/03/03 09:48:35 garyo" + + +import os +import errno +import shutil +import stat +import time +import sys + +import SCons.Action +import SCons.Builder +import SCons.CacheDir +import SCons.Environment +import SCons.PathList +import SCons.Subst +import SCons.Tool + +# A placeholder for a default Environment (for fetching source files +# from source code management systems and the like). This must be +# initialized later, after the top-level directory is set by the calling +# interface. +_default_env = None + +# Lazily instantiate the default environment so the overhead of creating +# it doesn't apply when it's not needed. +def _fetch_DefaultEnvironment(*args, **kw): + """ + Returns the already-created default construction environment. + """ + global _default_env + return _default_env + +def DefaultEnvironment(*args, **kw): + """ + Initial public entry point for creating the default construction + Environment. + + After creating the environment, we overwrite our name + (DefaultEnvironment) with the _fetch_DefaultEnvironment() function, + which more efficiently returns the initialized default construction + environment without checking for its existence. + + (This function still exists with its _default_check because someone + else (*cough* Script/__init__.py *cough*) may keep a reference + to this function. So we can't use the fully functional idiom of + having the name originally be a something that *only* creates the + construction environment and then overwrites the name.) + """ + global _default_env + if not _default_env: + import SCons.Util + _default_env = SCons.Environment.Environment(*args, **kw) + if SCons.Util.md5: + _default_env.Decider('MD5') + else: + _default_env.Decider('timestamp-match') + global DefaultEnvironment + DefaultEnvironment = _fetch_DefaultEnvironment + _default_env._CacheDir_path = None + return _default_env + +# Emitters for setting the shared attribute on object files, +# and an action for checking that all of the source files +# going into a shared library are, in fact, shared. +def StaticObjectEmitter(target, source, env): + for tgt in target: + tgt.attributes.shared = None + return (target, source) + +def SharedObjectEmitter(target, source, env): + for tgt in target: + tgt.attributes.shared = 1 + return (target, source) + +def SharedFlagChecker(source, target, env): + same = env.subst('$STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME') + if same == '0' or same == '' or same == 'False': + for src in source: + try: + shared = src.attributes.shared + except AttributeError: + shared = None + if not shared: + raise SCons.Errors.UserError("Source file: %s is static and is not compatible with shared target: %s" % (src, target[0])) + +SharedCheck = SCons.Action.Action(SharedFlagChecker, None) + +# Some people were using these variable name before we made +# SourceFileScanner part of the public interface. Don't break their +# SConscript files until we've given them some fair warning and a +# transition period. +CScan = SCons.Tool.CScanner +DScan = SCons.Tool.DScanner +LaTeXScan = SCons.Tool.LaTeXScanner +ObjSourceScan = SCons.Tool.SourceFileScanner +ProgScan = SCons.Tool.ProgramScanner + +# These aren't really tool scanners, so they don't quite belong with +# the rest of those in Tool/__init__.py, but I'm not sure where else +# they should go. Leave them here for now. +import SCons.Scanner.Dir +DirScanner = SCons.Scanner.Dir.DirScanner() +DirEntryScanner = SCons.Scanner.Dir.DirEntryScanner() + +# Actions for common languages. +CAction = SCons.Action.Action("$CCCOM", "$CCCOMSTR") +ShCAction = SCons.Action.Action("$SHCCCOM", "$SHCCCOMSTR") +CXXAction = SCons.Action.Action("$CXXCOM", "$CXXCOMSTR") +ShCXXAction = SCons.Action.Action("$SHCXXCOM", "$SHCXXCOMSTR") + +ASAction = SCons.Action.Action("$ASCOM", "$ASCOMSTR") +ASPPAction = SCons.Action.Action("$ASPPCOM", "$ASPPCOMSTR") + +LinkAction = SCons.Action.Action("$LINKCOM", "$LINKCOMSTR") +ShLinkAction = SCons.Action.Action("$SHLINKCOM", "$SHLINKCOMSTR") + +LdModuleLinkAction = SCons.Action.Action("$LDMODULECOM", "$LDMODULECOMSTR") + +# Common tasks that we allow users to perform in platform-independent +# ways by creating ActionFactory instances. +ActionFactory = SCons.Action.ActionFactory + +def get_paths_str(dest): + # If dest is a list, we need to manually call str() on each element + if SCons.Util.is_List(dest): + elem_strs = [] + for element in dest: + elem_strs.append('"' + str(element) + '"') + return '[' + ', '.join(elem_strs) + ']' + else: + return '"' + str(dest) + '"' + +def chmod_func(dest, mode): + SCons.Node.FS.invalidate_node_memos(dest) + if not SCons.Util.is_List(dest): + dest = [dest] + for element in dest: + os.chmod(str(element), mode) + +def chmod_strfunc(dest, mode): + return 'Chmod(%s, 0%o)' % (get_paths_str(dest), mode) + +Chmod = ActionFactory(chmod_func, chmod_strfunc) + +def copy_func(dest, src): + SCons.Node.FS.invalidate_node_memos(dest) + if SCons.Util.is_List(src) and os.path.isdir(dest): + for file in src: + shutil.copy2(file, dest) + return 0 + elif os.path.isfile(src): + return shutil.copy2(src, dest) + else: + return shutil.copytree(src, dest, 1) + +Copy = ActionFactory(copy_func, + lambda dest, src: 'Copy("%s", "%s")' % (dest, src), + convert=str) + +def delete_func(dest, must_exist=0): + SCons.Node.FS.invalidate_node_memos(dest) + if not SCons.Util.is_List(dest): + dest = [dest] + for entry in dest: + entry = str(entry) + # os.path.exists returns False with broken links that exist + entry_exists = os.path.exists(entry) or os.path.islink(entry) + if not entry_exists and not must_exist: + continue + # os.path.isdir returns True when entry is a link to a dir + if os.path.isdir(entry) and not os.path.islink(entry): + shutil.rmtree(entry, 1) + continue + os.unlink(entry) + +def delete_strfunc(dest, must_exist=0): + return 'Delete(%s)' % get_paths_str(dest) + +Delete = ActionFactory(delete_func, delete_strfunc) + +def mkdir_func(dest): + SCons.Node.FS.invalidate_node_memos(dest) + if not SCons.Util.is_List(dest): + dest = [dest] + for entry in dest: + try: + os.makedirs(str(entry)) + except os.error, e: + p = str(entry) + if (e.args[0] == errno.EEXIST or + (sys.platform=='win32' and e.args[0]==183)) \ + and os.path.isdir(str(entry)): + pass # not an error if already exists + else: + raise + +Mkdir = ActionFactory(mkdir_func, + lambda dir: 'Mkdir(%s)' % get_paths_str(dir)) + +def move_func(dest, src): + SCons.Node.FS.invalidate_node_memos(dest) + SCons.Node.FS.invalidate_node_memos(src) + shutil.move(src, dest) + +Move = ActionFactory(move_func, + lambda dest, src: 'Move("%s", "%s")' % (dest, src), + convert=str) + +def touch_func(dest): + SCons.Node.FS.invalidate_node_memos(dest) + if not SCons.Util.is_List(dest): + dest = [dest] + for file in dest: + file = str(file) + mtime = int(time.time()) + if os.path.exists(file): + atime = os.path.getatime(file) + else: + open(file, 'w') + atime = mtime + os.utime(file, (atime, mtime)) + +Touch = ActionFactory(touch_func, + lambda file: 'Touch(%s)' % get_paths_str(file)) + +# Internal utility functions + +def _concat(prefix, list, suffix, env, f=lambda x: x, target=None, source=None): + """ + Creates a new list from 'list' by first interpolating each element + in the list using the 'env' dictionary and then calling f on the + list, and finally calling _concat_ixes to concatenate 'prefix' and + 'suffix' onto each element of the list. + """ + if not list: + return list + + l = f(SCons.PathList.PathList(list).subst_path(env, target, source)) + if l is not None: + list = l + + return _concat_ixes(prefix, list, suffix, env) + +def _concat_ixes(prefix, list, suffix, env): + """ + Creates a new list from 'list' by concatenating the 'prefix' and + 'suffix' arguments onto each element of the list. A trailing space + on 'prefix' or leading space on 'suffix' will cause them to be put + into separate list elements rather than being concatenated. + """ + + result = [] + + # ensure that prefix and suffix are strings + prefix = str(env.subst(prefix, SCons.Subst.SUBST_RAW)) + suffix = str(env.subst(suffix, SCons.Subst.SUBST_RAW)) + + for x in list: + if isinstance(x, SCons.Node.FS.File): + result.append(x) + continue + x = str(x) + if x: + + if prefix: + if prefix[-1] == ' ': + result.append(prefix[:-1]) + elif x[:len(prefix)] != prefix: + x = prefix + x + + result.append(x) + + if suffix: + if suffix[0] == ' ': + result.append(suffix[1:]) + elif x[-len(suffix):] != suffix: + result[-1] = result[-1]+suffix + + return result + +def _stripixes(prefix, itms, suffix, stripprefixes, stripsuffixes, env, c=None): + """ + This is a wrapper around _concat()/_concat_ixes() that checks for + the existence of prefixes or suffixes on list items and strips them + where it finds them. This is used by tools (like the GNU linker) + that need to turn something like 'libfoo.a' into '-lfoo'. + """ + + if not itms: + return itms + + if not callable(c): + env_c = env['_concat'] + if env_c != _concat and callable(env_c): + # There's a custom _concat() method in the construction + # environment, and we've allowed people to set that in + # the past (see test/custom-concat.py), so preserve the + # backwards compatibility. + c = env_c + else: + c = _concat_ixes + + stripprefixes = list(map(env.subst, SCons.Util.flatten(stripprefixes))) + stripsuffixes = list(map(env.subst, SCons.Util.flatten(stripsuffixes))) + + stripped = [] + for l in SCons.PathList.PathList(itms).subst_path(env, None, None): + if isinstance(l, SCons.Node.FS.File): + stripped.append(l) + continue + + if not SCons.Util.is_String(l): + l = str(l) + + for stripprefix in stripprefixes: + lsp = len(stripprefix) + if l[:lsp] == stripprefix: + l = l[lsp:] + # Do not strip more than one prefix + break + + for stripsuffix in stripsuffixes: + lss = len(stripsuffix) + if l[-lss:] == stripsuffix: + l = l[:-lss] + # Do not strip more than one suffix + break + + stripped.append(l) + + return c(prefix, stripped, suffix, env) + +def processDefines(defs): + """process defines, resolving strings, lists, dictionaries, into a list of + strings + """ + if SCons.Util.is_List(defs): + l = [] + for d in defs: + if d is None: + continue + elif SCons.Util.is_List(d) or isinstance(d, tuple): + if len(d) >= 2: + l.append(str(d[0]) + '=' + str(d[1])) + else: + l.append(str(d[0])) + elif SCons.Util.is_Dict(d): + for macro,value in d.iteritems(): + if value is not None: + l.append(str(macro) + '=' + str(value)) + else: + l.append(str(macro)) + elif SCons.Util.is_String(d): + l.append(str(d)) + else: + raise SCons.Errors.UserError("DEFINE %s is not a list, dict, string or None."%repr(d)) + elif SCons.Util.is_Dict(defs): + # The items in a dictionary are stored in random order, but + # if the order of the command-line options changes from + # invocation to invocation, then the signature of the command + # line will change and we'll get random unnecessary rebuilds. + # Consequently, we have to sort the keys to ensure a + # consistent order... + l = [] + for k,v in sorted(defs.items()): + if v is None: + l.append(str(k)) + else: + l.append(str(k) + '=' + str(v)) + else: + l = [str(defs)] + return l + +def _defines(prefix, defs, suffix, env, c=_concat_ixes): + """A wrapper around _concat_ixes that turns a list or string + into a list of C preprocessor command-line definitions. + """ + + return c(prefix, env.subst_path(processDefines(defs)), suffix, env) + +class NullCmdGenerator(object): + """This is a callable class that can be used in place of other + command generators if you don't want them to do anything. + + The __call__ method for this class simply returns the thing + you instantiated it with. + + Example usage: + env["DO_NOTHING"] = NullCmdGenerator + env["LINKCOM"] = "${DO_NOTHING('$LINK $SOURCES $TARGET')}" + """ + + def __init__(self, cmd): + self.cmd = cmd + + def __call__(self, target, source, env, for_signature=None): + return self.cmd + +class Variable_Method_Caller(object): + """A class for finding a construction variable on the stack and + calling one of its methods. + + We use this to support "construction variables" in our string + eval()s that actually stand in for methods--specifically, use + of "RDirs" in call to _concat that should actually execute the + "TARGET.RDirs" method. (We used to support this by creating a little + "build dictionary" that mapped RDirs to the method, but this got in + the way of Memoizing construction environments, because we had to + create new environment objects to hold the variables.) + """ + def __init__(self, variable, method): + self.variable = variable + self.method = method + def __call__(self, *args, **kw): + try: 1//0 + except ZeroDivisionError: + # Don't start iterating with the current stack-frame to + # prevent creating reference cycles (f_back is safe). + frame = sys.exc_info()[2].tb_frame.f_back + variable = self.variable + while frame: + if variable in frame.f_locals: + v = frame.f_locals[variable] + if v: + method = getattr(v, self.method) + return method(*args, **kw) + frame = frame.f_back + return None + +ConstructionEnvironment = { + 'BUILDERS' : {}, + 'SCANNERS' : [], + 'CONFIGUREDIR' : '#/.sconf_temp', + 'CONFIGURELOG' : '#/config.log', + 'CPPSUFFIXES' : SCons.Tool.CSuffixes, + 'DSUFFIXES' : SCons.Tool.DSuffixes, + 'ENV' : {}, + 'IDLSUFFIXES' : SCons.Tool.IDLSuffixes, +# 'LATEXSUFFIXES' : SCons.Tool.LaTeXSuffixes, # moved to the TeX tools generate functions + '_concat' : _concat, + '_defines' : _defines, + '_stripixes' : _stripixes, + '_LIBFLAGS' : '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}', + '_LIBDIRFLAGS' : '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', + '_CPPINCFLAGS' : '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)', + '_CPPDEFFLAGS' : '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}', + 'TEMPFILE' : NullCmdGenerator, + 'Dir' : Variable_Method_Caller('TARGET', 'Dir'), + 'Dirs' : Variable_Method_Caller('TARGET', 'Dirs'), + 'File' : Variable_Method_Caller('TARGET', 'File'), + 'RDirs' : Variable_Method_Caller('TARGET', 'RDirs'), +} + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Environment.py b/scons/scons-local-2.3.0/SCons/Environment.py new file mode 100644 index 000000000..833f3ccb9 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Environment.py @@ -0,0 +1,2418 @@ +"""SCons.Environment + +Base class for construction Environments. These are +the primary objects used to communicate dependency and +construction information to the build engine. + +Keyword arguments supplied when the construction Environment +is created are construction variables used to initialize the +Environment +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Environment.py 2013/03/03 09:48:35 garyo" + + +import copy +import os +import sys +import re +import shlex +from collections import UserDict + +import SCons.Action +import SCons.Builder +from SCons.Debug import logInstanceCreation +import SCons.Defaults +import SCons.Errors +import SCons.Memoize +import SCons.Node +import SCons.Node.Alias +import SCons.Node.FS +import SCons.Node.Python +import SCons.Platform +import SCons.SConf +import SCons.SConsign +import SCons.Subst +import SCons.Tool +import SCons.Util +import SCons.Warnings + +class _Null(object): + pass + +_null = _Null + +_warn_copy_deprecated = True +_warn_source_signatures_deprecated = True +_warn_target_signatures_deprecated = True + +CleanTargets = {} +CalculatorArgs = {} + +semi_deepcopy = SCons.Util.semi_deepcopy +semi_deepcopy_dict = SCons.Util.semi_deepcopy_dict + +# Pull UserError into the global name space for the benefit of +# Environment().SourceSignatures(), which has some import statements +# which seem to mess up its ability to reference SCons directly. +UserError = SCons.Errors.UserError + +def alias_builder(env, target, source): + pass + +AliasBuilder = SCons.Builder.Builder(action = alias_builder, + target_factory = SCons.Node.Alias.default_ans.Alias, + source_factory = SCons.Node.FS.Entry, + multi = 1, + is_explicit = None, + name='AliasBuilder') + +def apply_tools(env, tools, toolpath): + # Store the toolpath in the Environment. + if toolpath is not None: + env['toolpath'] = toolpath + + if not tools: + return + # Filter out null tools from the list. + for tool in [_f for _f in tools if _f]: + if SCons.Util.is_List(tool) or isinstance(tool, tuple): + toolname = tool[0] + toolargs = tool[1] # should be a dict of kw args + tool = env.Tool(toolname, **toolargs) + else: + env.Tool(tool) + +# These names are (or will be) controlled by SCons; users should never +# set or override them. This warning can optionally be turned off, +# but scons will still ignore the illegal variable names even if it's off. +reserved_construction_var_names = [ + 'CHANGED_SOURCES', + 'CHANGED_TARGETS', + 'SOURCE', + 'SOURCES', + 'TARGET', + 'TARGETS', + 'UNCHANGED_SOURCES', + 'UNCHANGED_TARGETS', +] + +future_reserved_construction_var_names = [ + #'HOST_OS', + #'HOST_ARCH', + #'HOST_CPU', + ] + +def copy_non_reserved_keywords(dict): + result = semi_deepcopy(dict) + for k in result.keys(): + if k in reserved_construction_var_names: + msg = "Ignoring attempt to set reserved variable `$%s'" + SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg % k) + del result[k] + return result + +def _set_reserved(env, key, value): + msg = "Ignoring attempt to set reserved variable `$%s'" + SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg % key) + +def _set_future_reserved(env, key, value): + env._dict[key] = value + msg = "`$%s' will be reserved in a future release and setting it will become ignored" + SCons.Warnings.warn(SCons.Warnings.FutureReservedVariableWarning, msg % key) + +def _set_BUILDERS(env, key, value): + try: + bd = env._dict[key] + for k in bd.keys(): + del bd[k] + except KeyError: + bd = BuilderDict(kwbd, env) + env._dict[key] = bd + for k, v in value.items(): + if not SCons.Builder.is_a_Builder(v): + raise SCons.Errors.UserError('%s is not a Builder.' % repr(v)) + bd.update(value) + +def _del_SCANNERS(env, key): + del env._dict[key] + env.scanner_map_delete() + +def _set_SCANNERS(env, key, value): + env._dict[key] = value + env.scanner_map_delete() + +def _delete_duplicates(l, keep_last): + """Delete duplicates from a sequence, keeping the first or last.""" + seen={} + result=[] + if keep_last: # reverse in & out, then keep first + l.reverse() + for i in l: + try: + if i not in seen: + result.append(i) + seen[i]=1 + except TypeError: + # probably unhashable. Just keep it. + result.append(i) + if keep_last: + result.reverse() + return result + + + +# The following is partly based on code in a comment added by Peter +# Shannon at the following page (there called the "transplant" class): +# +# ASPN : Python Cookbook : Dynamically added methods to a class +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732 +# +# We had independently been using the idiom as BuilderWrapper, but +# factoring out the common parts into this base class, and making +# BuilderWrapper a subclass that overrides __call__() to enforce specific +# Builder calling conventions, simplified some of our higher-layer code. + +class MethodWrapper(object): + """ + A generic Wrapper class that associates a method (which can + actually be any callable) with an object. As part of creating this + MethodWrapper object an attribute with the specified (by default, + the name of the supplied method) is added to the underlying object. + When that new "method" is called, our __call__() method adds the + object as the first argument, simulating the Python behavior of + supplying "self" on method calls. + + We hang on to the name by which the method was added to the underlying + base class so that we can provide a method to "clone" ourselves onto + a new underlying object being copied (without which we wouldn't need + to save that info). + """ + def __init__(self, object, method, name=None): + if name is None: + name = method.__name__ + self.object = object + self.method = method + self.name = name + setattr(self.object, name, self) + + def __call__(self, *args, **kwargs): + nargs = (self.object,) + args + return self.method(*nargs, **kwargs) + + def clone(self, new_object): + """ + Returns an object that re-binds the underlying "method" to + the specified new object. + """ + return self.__class__(new_object, self.method, self.name) + +class BuilderWrapper(MethodWrapper): + """ + A MethodWrapper subclass that that associates an environment with + a Builder. + + This mainly exists to wrap the __call__() function so that all calls + to Builders can have their argument lists massaged in the same way + (treat a lone argument as the source, treat two arguments as target + then source, make sure both target and source are lists) without + having to have cut-and-paste code to do it. + + As a bit of obsessive backwards compatibility, we also intercept + attempts to get or set the "env" or "builder" attributes, which were + the names we used before we put the common functionality into the + MethodWrapper base class. We'll keep this around for a while in case + people shipped Tool modules that reached into the wrapper (like the + Tool/qt.py module does, or did). There shouldn't be a lot attribute + fetching or setting on these, so a little extra work shouldn't hurt. + """ + def __call__(self, target=None, source=_null, *args, **kw): + if source is _null: + source = target + target = None + if target is not None and not SCons.Util.is_List(target): + target = [target] + if source is not None and not SCons.Util.is_List(source): + source = [source] + return MethodWrapper.__call__(self, target, source, *args, **kw) + + def __repr__(self): + return '' % repr(self.name) + + def __str__(self): + return self.__repr__() + + def __getattr__(self, name): + if name == 'env': + return self.object + elif name == 'builder': + return self.method + else: + raise AttributeError(name) + + def __setattr__(self, name, value): + if name == 'env': + self.object = value + elif name == 'builder': + self.method = value + else: + self.__dict__[name] = value + + # This allows a Builder to be executed directly + # through the Environment to which it's attached. + # In practice, we shouldn't need this, because + # builders actually get executed through a Node. + # But we do have a unit test for this, and can't + # yet rule out that it would be useful in the + # future, so leave it for now. + #def execute(self, **kw): + # kw['env'] = self.env + # self.builder.execute(**kw) + +class BuilderDict(UserDict): + """This is a dictionary-like class used by an Environment to hold + the Builders. We need to do this because every time someone changes + the Builders in the Environment's BUILDERS dictionary, we must + update the Environment's attributes.""" + def __init__(self, dict, env): + # Set self.env before calling the superclass initialization, + # because it will end up calling our other methods, which will + # need to point the values in this dictionary to self.env. + self.env = env + UserDict.__init__(self, dict) + + def __semi_deepcopy__(self): + # These cannot be copied since they would both modify the same builder object, and indeed + # just copying would modify the original builder + raise TypeError( 'cannot semi_deepcopy a BuilderDict' ) + + def __setitem__(self, item, val): + try: + method = getattr(self.env, item).method + except AttributeError: + pass + else: + self.env.RemoveMethod(method) + UserDict.__setitem__(self, item, val) + BuilderWrapper(self.env, val, item) + + def __delitem__(self, item): + UserDict.__delitem__(self, item) + delattr(self.env, item) + + def update(self, dict): + for i, v in dict.items(): + self.__setitem__(i, v) + + + +_is_valid_var = re.compile(r'[_a-zA-Z]\w*$') + +def is_valid_construction_var(varstr): + """Return if the specified string is a legitimate construction + variable. + """ + return _is_valid_var.match(varstr) + + + +class SubstitutionEnvironment(object): + """Base class for different flavors of construction environments. + + This class contains a minimal set of methods that handle contruction + variable expansion and conversion of strings to Nodes, which may or + may not be actually useful as a stand-alone class. Which methods + ended up in this class is pretty arbitrary right now. They're + basically the ones which we've empirically determined are common to + the different construction environment subclasses, and most of the + others that use or touch the underlying dictionary of construction + variables. + + Eventually, this class should contain all the methods that we + determine are necessary for a "minimal" interface to the build engine. + A full "native Python" SCons environment has gotten pretty heavyweight + with all of the methods and Tools and construction variables we've + jammed in there, so it would be nice to have a lighter weight + alternative for interfaces that don't need all of the bells and + whistles. (At some point, we'll also probably rename this class + "Base," since that more reflects what we want this class to become, + but because we've released comments that tell people to subclass + Environment.Base to create their own flavors of construction + environment, we'll save that for a future refactoring when this + class actually becomes useful.) + """ + + if SCons.Memoize.use_memoizer: + __metaclass__ = SCons.Memoize.Memoized_Metaclass + + def __init__(self, **kw): + """Initialization of an underlying SubstitutionEnvironment class. + """ + if __debug__: logInstanceCreation(self, 'Environment.SubstitutionEnvironment') + self.fs = SCons.Node.FS.get_default_fs() + self.ans = SCons.Node.Alias.default_ans + self.lookup_list = SCons.Node.arg2nodes_lookups + self._dict = kw.copy() + self._init_special() + self.added_methods = [] + #self._memo = {} + + def _init_special(self): + """Initial the dispatch tables for special handling of + special construction variables.""" + self._special_del = {} + self._special_del['SCANNERS'] = _del_SCANNERS + + self._special_set = {} + for key in reserved_construction_var_names: + self._special_set[key] = _set_reserved + for key in future_reserved_construction_var_names: + self._special_set[key] = _set_future_reserved + self._special_set['BUILDERS'] = _set_BUILDERS + self._special_set['SCANNERS'] = _set_SCANNERS + + # Freeze the keys of self._special_set in a list for use by + # methods that need to check. (Empirically, list scanning has + # gotten better than dict.has_key() in Python 2.5.) + self._special_set_keys = list(self._special_set.keys()) + + def __cmp__(self, other): + return cmp(self._dict, other._dict) + + def __delitem__(self, key): + special = self._special_del.get(key) + if special: + special(self, key) + else: + del self._dict[key] + + def __getitem__(self, key): + return self._dict[key] + + def __setitem__(self, key, value): + # This is heavily used. This implementation is the best we have + # according to the timings in bench/env.__setitem__.py. + # + # The "key in self._special_set_keys" test here seems to perform + # pretty well for the number of keys we have. A hard-coded + # list works a little better in Python 2.5, but that has the + # disadvantage of maybe getting out of sync if we ever add more + # variable names. Using self._special_set.has_key() works a + # little better in Python 2.4, but is worse than this test. + # So right now it seems like a good trade-off, but feel free to + # revisit this with bench/env.__setitem__.py as needed (and + # as newer versions of Python come out). + if key in self._special_set_keys: + self._special_set[key](self, key, value) + else: + # If we already have the entry, then it's obviously a valid + # key and we don't need to check. If we do check, using a + # global, pre-compiled regular expression directly is more + # efficient than calling another function or a method. + if key not in self._dict \ + and not _is_valid_var.match(key): + raise SCons.Errors.UserError("Illegal construction variable `%s'" % key) + self._dict[key] = value + + def get(self, key, default=None): + """Emulates the get() method of dictionaries.""" + return self._dict.get(key, default) + + def has_key(self, key): + return key in self._dict + + def __contains__(self, key): + return self._dict.__contains__(key) + + def items(self): + return list(self._dict.items()) + + def arg2nodes(self, args, node_factory=_null, lookup_list=_null, **kw): + if node_factory is _null: + node_factory = self.fs.File + if lookup_list is _null: + lookup_list = self.lookup_list + + if not args: + return [] + + args = SCons.Util.flatten(args) + + nodes = [] + for v in args: + if SCons.Util.is_String(v): + n = None + for l in lookup_list: + n = l(v) + if n is not None: + break + if n is not None: + if SCons.Util.is_String(n): + # n = self.subst(n, raw=1, **kw) + kw['raw'] = 1 + n = self.subst(n, **kw) + if node_factory: + n = node_factory(n) + if SCons.Util.is_List(n): + nodes.extend(n) + else: + nodes.append(n) + elif node_factory: + # v = node_factory(self.subst(v, raw=1, **kw)) + kw['raw'] = 1 + v = node_factory(self.subst(v, **kw)) + if SCons.Util.is_List(v): + nodes.extend(v) + else: + nodes.append(v) + else: + nodes.append(v) + + return nodes + + def gvars(self): + return self._dict + + def lvars(self): + return {} + + def subst(self, string, raw=0, target=None, source=None, conv=None, executor=None): + """Recursively interpolates construction variables from the + Environment into the specified string, returning the expanded + result. Construction variables are specified by a $ prefix + in the string and begin with an initial underscore or + alphabetic character followed by any number of underscores + or alphanumeric characters. The construction variable names + may be surrounded by curly braces to separate the name from + trailing characters. + """ + gvars = self.gvars() + lvars = self.lvars() + lvars['__env__'] = self + if executor: + lvars.update(executor.get_lvars()) + return SCons.Subst.scons_subst(string, self, raw, target, source, gvars, lvars, conv) + + def subst_kw(self, kw, raw=0, target=None, source=None): + nkw = {} + for k, v in kw.items(): + k = self.subst(k, raw, target, source) + if SCons.Util.is_String(v): + v = self.subst(v, raw, target, source) + nkw[k] = v + return nkw + + def subst_list(self, string, raw=0, target=None, source=None, conv=None, executor=None): + """Calls through to SCons.Subst.scons_subst_list(). See + the documentation for that function.""" + gvars = self.gvars() + lvars = self.lvars() + lvars['__env__'] = self + if executor: + lvars.update(executor.get_lvars()) + return SCons.Subst.scons_subst_list(string, self, raw, target, source, gvars, lvars, conv) + + def subst_path(self, path, target=None, source=None): + """Substitute a path list, turning EntryProxies into Nodes + and leaving Nodes (and other objects) as-is.""" + + if not SCons.Util.is_List(path): + path = [path] + + def s(obj): + """This is the "string conversion" routine that we have our + substitutions use to return Nodes, not strings. This relies + on the fact that an EntryProxy object has a get() method that + returns the underlying Node that it wraps, which is a bit of + architectural dependence that we might need to break or modify + in the future in response to additional requirements.""" + try: + get = obj.get + except AttributeError: + obj = SCons.Util.to_String_for_subst(obj) + else: + obj = get() + return obj + + r = [] + for p in path: + if SCons.Util.is_String(p): + p = self.subst(p, target=target, source=source, conv=s) + if SCons.Util.is_List(p): + if len(p) == 1: + p = p[0] + else: + # We have an object plus a string, or multiple + # objects that we need to smush together. No choice + # but to make them into a string. + p = ''.join(map(SCons.Util.to_String_for_subst, p)) + else: + p = s(p) + r.append(p) + return r + + subst_target_source = subst + + def backtick(self, command): + import subprocess + # common arguments + kw = { 'stdin' : 'devnull', + 'stdout' : subprocess.PIPE, + 'stderr' : subprocess.PIPE, + 'universal_newlines' : True, + } + # if the command is a list, assume it's been quoted + # othewise force a shell + if not SCons.Util.is_List(command): kw['shell'] = True + # run constructed command + p = SCons.Action._subproc(self, command, **kw) + out,err = p.communicate() + status = p.wait() + if err: + sys.stderr.write(unicode(err)) + if status: + raise OSError("'%s' exited %d" % (command, status)) + return out + + def AddMethod(self, function, name=None): + """ + Adds the specified function as a method of this construction + environment with the specified name. If the name is omitted, + the default name is the name of the function itself. + """ + method = MethodWrapper(self, function, name) + self.added_methods.append(method) + + def RemoveMethod(self, function): + """ + Removes the specified function's MethodWrapper from the + added_methods list, so we don't re-bind it when making a clone. + """ + self.added_methods = [dm for dm in self.added_methods if not dm.method is function] + + def Override(self, overrides): + """ + Produce a modified environment whose variables are overriden by + the overrides dictionaries. "overrides" is a dictionary that + will override the variables of this environment. + + This function is much more efficient than Clone() or creating + a new Environment because it doesn't copy the construction + environment dictionary, it just wraps the underlying construction + environment, and doesn't even create a wrapper object if there + are no overrides. + """ + if not overrides: return self + o = copy_non_reserved_keywords(overrides) + if not o: return self + overrides = {} + merges = None + for key, value in o.items(): + if key == 'parse_flags': + merges = value + else: + overrides[key] = SCons.Subst.scons_subst_once(value, self, key) + env = OverrideEnvironment(self, overrides) + if merges: env.MergeFlags(merges) + return env + + def ParseFlags(self, *flags): + """ + Parse the set of flags and return a dict with the flags placed + in the appropriate entry. The flags are treated as a typical + set of command-line flags for a GNU-like toolchain and used to + populate the entries in the dict immediately below. If one of + the flag strings begins with a bang (exclamation mark), it is + assumed to be a command and the rest of the string is executed; + the result of that evaluation is then added to the dict. + """ + dict = { + 'ASFLAGS' : SCons.Util.CLVar(''), + 'CFLAGS' : SCons.Util.CLVar(''), + 'CCFLAGS' : SCons.Util.CLVar(''), + 'CXXFLAGS' : SCons.Util.CLVar(''), + 'CPPDEFINES' : [], + 'CPPFLAGS' : SCons.Util.CLVar(''), + 'CPPPATH' : [], + 'FRAMEWORKPATH' : SCons.Util.CLVar(''), + 'FRAMEWORKS' : SCons.Util.CLVar(''), + 'LIBPATH' : [], + 'LIBS' : [], + 'LINKFLAGS' : SCons.Util.CLVar(''), + 'RPATH' : [], + } + + def do_parse(arg): + # if arg is a sequence, recurse with each element + if not arg: + return + + if not SCons.Util.is_String(arg): + for t in arg: do_parse(t) + return + + # if arg is a command, execute it + if arg[0] == '!': + arg = self.backtick(arg[1:]) + + # utility function to deal with -D option + def append_define(name, dict = dict): + t = name.split('=') + if len(t) == 1: + dict['CPPDEFINES'].append(name) + else: + dict['CPPDEFINES'].append([t[0], '='.join(t[1:])]) + + # Loop through the flags and add them to the appropriate option. + # This tries to strike a balance between checking for all possible + # flags and keeping the logic to a finite size, so it doesn't + # check for some that don't occur often. It particular, if the + # flag is not known to occur in a config script and there's a way + # of passing the flag to the right place (by wrapping it in a -W + # flag, for example) we don't check for it. Note that most + # preprocessor options are not handled, since unhandled options + # are placed in CCFLAGS, so unless the preprocessor is invoked + # separately, these flags will still get to the preprocessor. + # Other options not currently handled: + # -iqoutedir (preprocessor search path) + # -u symbol (linker undefined symbol) + # -s (linker strip files) + # -static* (linker static binding) + # -shared* (linker dynamic binding) + # -symbolic (linker global binding) + # -R dir (deprecated linker rpath) + # IBM compilers may also accept -qframeworkdir=foo + + params = shlex.split(arg) + append_next_arg_to = None # for multi-word args + for arg in params: + if append_next_arg_to: + if append_next_arg_to == 'CPPDEFINES': + append_define(arg) + elif append_next_arg_to == '-include': + t = ('-include', self.fs.File(arg)) + dict['CCFLAGS'].append(t) + elif append_next_arg_to == '-isysroot': + t = ('-isysroot', arg) + dict['CCFLAGS'].append(t) + dict['LINKFLAGS'].append(t) + elif append_next_arg_to == '-arch': + t = ('-arch', arg) + dict['CCFLAGS'].append(t) + dict['LINKFLAGS'].append(t) + else: + dict[append_next_arg_to].append(arg) + append_next_arg_to = None + elif not arg[0] in ['-', '+']: + dict['LIBS'].append(self.fs.File(arg)) + elif arg == '-dylib_file': + dict['LINKFLAGS'].append(arg) + append_next_arg_to = 'LINKFLAGS' + elif arg[:2] == '-L': + if arg[2:]: + dict['LIBPATH'].append(arg[2:]) + else: + append_next_arg_to = 'LIBPATH' + elif arg[:2] == '-l': + if arg[2:]: + dict['LIBS'].append(arg[2:]) + else: + append_next_arg_to = 'LIBS' + elif arg[:2] == '-I': + if arg[2:]: + dict['CPPPATH'].append(arg[2:]) + else: + append_next_arg_to = 'CPPPATH' + elif arg[:4] == '-Wa,': + dict['ASFLAGS'].append(arg[4:]) + dict['CCFLAGS'].append(arg) + elif arg[:4] == '-Wl,': + if arg[:11] == '-Wl,-rpath=': + dict['RPATH'].append(arg[11:]) + elif arg[:7] == '-Wl,-R,': + dict['RPATH'].append(arg[7:]) + elif arg[:6] == '-Wl,-R': + dict['RPATH'].append(arg[6:]) + else: + dict['LINKFLAGS'].append(arg) + elif arg[:4] == '-Wp,': + dict['CPPFLAGS'].append(arg) + elif arg[:2] == '-D': + if arg[2:]: + append_define(arg[2:]) + else: + append_next_arg_to = 'CPPDEFINES' + elif arg == '-framework': + append_next_arg_to = 'FRAMEWORKS' + elif arg[:14] == '-frameworkdir=': + dict['FRAMEWORKPATH'].append(arg[14:]) + elif arg[:2] == '-F': + if arg[2:]: + dict['FRAMEWORKPATH'].append(arg[2:]) + else: + append_next_arg_to = 'FRAMEWORKPATH' + elif arg in ['-mno-cygwin', + '-pthread', + '-openmp', + '-fopenmp']: + dict['CCFLAGS'].append(arg) + dict['LINKFLAGS'].append(arg) + elif arg == '-mwindows': + dict['LINKFLAGS'].append(arg) + elif arg[:5] == '-std=': + if arg[5:].find('++')!=-1: + key='CXXFLAGS' + else: + key='CFLAGS' + dict[key].append(arg) + elif arg[0] == '+': + dict['CCFLAGS'].append(arg) + dict['LINKFLAGS'].append(arg) + elif arg in ['-include', '-isysroot', '-arch']: + append_next_arg_to = arg + else: + dict['CCFLAGS'].append(arg) + + for arg in flags: + do_parse(arg) + return dict + + def MergeFlags(self, args, unique=1, dict=None): + """ + Merge the dict in args into the construction variables of this + env, or the passed-in dict. If args is not a dict, it is + converted into a dict using ParseFlags. If unique is not set, + the flags are appended rather than merged. + """ + + if dict is None: + dict = self + if not SCons.Util.is_Dict(args): + args = self.ParseFlags(args) + if not unique: + self.Append(**args) + return self + for key, value in args.items(): + if not value: + continue + try: + orig = self[key] + except KeyError: + orig = value + else: + if not orig: + orig = value + elif value: + # Add orig and value. The logic here was lifted from + # part of env.Append() (see there for a lot of comments + # about the order in which things are tried) and is + # used mainly to handle coercion of strings to CLVar to + # "do the right thing" given (e.g.) an original CCFLAGS + # string variable like '-pipe -Wall'. + try: + orig = orig + value + except (KeyError, TypeError): + try: + add_to_orig = orig.append + except AttributeError: + value.insert(0, orig) + orig = value + else: + add_to_orig(value) + t = [] + if key[-4:] == 'PATH': + ### keep left-most occurence + for v in orig: + if v not in t: + t.append(v) + else: + ### keep right-most occurence + orig.reverse() + for v in orig: + if v not in t: + t.insert(0, v) + self[key] = t + return self + +# def MergeShellPaths(self, args, prepend=1): +# """ +# Merge the dict in args into the shell environment in env['ENV']. +# Shell path elements are appended or prepended according to prepend. + +# Uses Pre/AppendENVPath, so it always appends or prepends uniquely. + +# Example: env.MergeShellPaths({'LIBPATH': '/usr/local/lib'}) +# prepends /usr/local/lib to env['ENV']['LIBPATH']. +# """ + +# for pathname, pathval in args.items(): +# if not pathval: +# continue +# if prepend: +# self.PrependENVPath(pathname, pathval) +# else: +# self.AppendENVPath(pathname, pathval) + + +def default_decide_source(dependency, target, prev_ni): + f = SCons.Defaults.DefaultEnvironment().decide_source + return f(dependency, target, prev_ni) + +def default_decide_target(dependency, target, prev_ni): + f = SCons.Defaults.DefaultEnvironment().decide_target + return f(dependency, target, prev_ni) + +def default_copy_from_cache(src, dst): + f = SCons.Defaults.DefaultEnvironment().copy_from_cache + return f(src, dst) + +class Base(SubstitutionEnvironment): + """Base class for "real" construction Environments. These are the + primary objects used to communicate dependency and construction + information to the build engine. + + Keyword arguments supplied when the construction Environment + is created are construction variables used to initialize the + Environment. + """ + + memoizer_counters = [] + + ####################################################################### + # This is THE class for interacting with the SCons build engine, + # and it contains a lot of stuff, so we're going to try to keep this + # a little organized by grouping the methods. + ####################################################################### + + ####################################################################### + # Methods that make an Environment act like a dictionary. These have + # the expected standard names for Python mapping objects. Note that + # we don't actually make an Environment a subclass of UserDict for + # performance reasons. Note also that we only supply methods for + # dictionary functionality that we actually need and use. + ####################################################################### + + def __init__(self, + platform=None, + tools=None, + toolpath=None, + variables=None, + parse_flags = None, + **kw): + """ + Initialization of a basic SCons construction environment, + including setting up special construction variables like BUILDER, + PLATFORM, etc., and searching for and applying available Tools. + + Note that we do *not* call the underlying base class + (SubsitutionEnvironment) initialization, because we need to + initialize things in a very specific order that doesn't work + with the much simpler base class initialization. + """ + if __debug__: logInstanceCreation(self, 'Environment.Base') + self._memo = {} + self.fs = SCons.Node.FS.get_default_fs() + self.ans = SCons.Node.Alias.default_ans + self.lookup_list = SCons.Node.arg2nodes_lookups + self._dict = semi_deepcopy(SCons.Defaults.ConstructionEnvironment) + self._init_special() + self.added_methods = [] + + # We don't use AddMethod, or define these as methods in this + # class, because we *don't* want these functions to be bound + # methods. They need to operate independently so that the + # settings will work properly regardless of whether a given + # target ends up being built with a Base environment or an + # OverrideEnvironment or what have you. + self.decide_target = default_decide_target + self.decide_source = default_decide_source + + self.copy_from_cache = default_copy_from_cache + + self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self) + + if platform is None: + platform = self._dict.get('PLATFORM', None) + if platform is None: + platform = SCons.Platform.Platform() + if SCons.Util.is_String(platform): + platform = SCons.Platform.Platform(platform) + self._dict['PLATFORM'] = str(platform) + platform(self) + + self._dict['HOST_OS'] = self._dict.get('HOST_OS',None) + self._dict['HOST_ARCH'] = self._dict.get('HOST_ARCH',None) + + # Now set defaults for TARGET_{OS|ARCH} + self._dict['TARGET_OS'] = self._dict.get('HOST_OS',None) + self._dict['TARGET_ARCH'] = self._dict.get('HOST_ARCH',None) + + + # Apply the passed-in and customizable variables to the + # environment before calling the tools, because they may use + # some of them during initialization. + if 'options' in kw: + # Backwards compatibility: they may stll be using the + # old "options" keyword. + variables = kw['options'] + del kw['options'] + self.Replace(**kw) + keys = list(kw.keys()) + if variables: + keys = keys + list(variables.keys()) + variables.Update(self) + + save = {} + for k in keys: + try: + save[k] = self._dict[k] + except KeyError: + # No value may have been set if they tried to pass in a + # reserved variable name like TARGETS. + pass + + SCons.Tool.Initializers(self) + + if tools is None: + tools = self._dict.get('TOOLS', None) + if tools is None: + tools = ['default'] + apply_tools(self, tools, toolpath) + + # Now restore the passed-in and customized variables + # to the environment, since the values the user set explicitly + # should override any values set by the tools. + for key, val in save.items(): + self._dict[key] = val + + # Finally, apply any flags to be merged in + if parse_flags: self.MergeFlags(parse_flags) + + ####################################################################### + # Utility methods that are primarily for internal use by SCons. + # These begin with lower-case letters. + ####################################################################### + + def get_builder(self, name): + """Fetch the builder with the specified name from the environment. + """ + try: + return self._dict['BUILDERS'][name] + except KeyError: + return None + + def get_CacheDir(self): + try: + path = self._CacheDir_path + except AttributeError: + path = SCons.Defaults.DefaultEnvironment()._CacheDir_path + try: + if path == self._last_CacheDir_path: + return self._last_CacheDir + except AttributeError: + pass + cd = SCons.CacheDir.CacheDir(path) + self._last_CacheDir_path = path + self._last_CacheDir = cd + return cd + + def get_factory(self, factory, default='File'): + """Return a factory function for creating Nodes for this + construction environment. + """ + name = default + try: + is_node = issubclass(factory, SCons.Node.FS.Base) + except TypeError: + # The specified factory isn't a Node itself--it's + # most likely None, or possibly a callable. + pass + else: + if is_node: + # The specified factory is a Node (sub)class. Try to + # return the FS method that corresponds to the Node's + # name--that is, we return self.fs.Dir if they want a Dir, + # self.fs.File for a File, etc. + try: name = factory.__name__ + except AttributeError: pass + else: factory = None + if not factory: + # They passed us None, or we picked up a name from a specified + # class, so return the FS method. (Note that we *don't* + # use our own self.{Dir,File} methods because that would + # cause env.subst() to be called twice on the file name, + # interfering with files that have $$ in them.) + factory = getattr(self.fs, name) + return factory + + memoizer_counters.append(SCons.Memoize.CountValue('_gsm')) + + def _gsm(self): + try: + return self._memo['_gsm'] + except KeyError: + pass + + result = {} + + try: + scanners = self._dict['SCANNERS'] + except KeyError: + pass + else: + # Reverse the scanner list so that, if multiple scanners + # claim they can scan the same suffix, earlier scanners + # in the list will overwrite later scanners, so that + # the result looks like a "first match" to the user. + if not SCons.Util.is_List(scanners): + scanners = [scanners] + else: + scanners = scanners[:] # copy so reverse() doesn't mod original + scanners.reverse() + for scanner in scanners: + for k in scanner.get_skeys(self): + if k and self['PLATFORM'] == 'win32': + k = k.lower() + result[k] = scanner + + self._memo['_gsm'] = result + + return result + + def get_scanner(self, skey): + """Find the appropriate scanner given a key (usually a file suffix). + """ + if skey and self['PLATFORM'] == 'win32': + skey = skey.lower() + return self._gsm().get(skey) + + def scanner_map_delete(self, kw=None): + """Delete the cached scanner map (if we need to). + """ + try: + del self._memo['_gsm'] + except KeyError: + pass + + def _update(self, dict): + """Update an environment's values directly, bypassing the normal + checks that occur when users try to set items. + """ + self._dict.update(dict) + + def get_src_sig_type(self): + try: + return self.src_sig_type + except AttributeError: + t = SCons.Defaults.DefaultEnvironment().src_sig_type + self.src_sig_type = t + return t + + def get_tgt_sig_type(self): + try: + return self.tgt_sig_type + except AttributeError: + t = SCons.Defaults.DefaultEnvironment().tgt_sig_type + self.tgt_sig_type = t + return t + + ####################################################################### + # Public methods for manipulating an Environment. These begin with + # upper-case letters. The essential characteristic of methods in + # this section is that they do *not* have corresponding same-named + # global functions. For example, a stand-alone Append() function + # makes no sense, because Append() is all about appending values to + # an Environment's construction variables. + ####################################################################### + + def Append(self, **kw): + """Append values to existing construction variables + in an Environment. + """ + kw = copy_non_reserved_keywords(kw) + for key, val in kw.items(): + # It would be easier on the eyes to write this using + # "continue" statements whenever we finish processing an item, + # but Python 1.5.2 apparently doesn't let you use "continue" + # within try:-except: blocks, so we have to nest our code. + try: + if key == 'CPPDEFINES' and SCons.Util.is_String(self._dict[key]): + self._dict[key] = [self._dict[key]] + orig = self._dict[key] + except KeyError: + # No existing variable in the environment, so just set + # it to the new value. + if key == 'CPPDEFINES' and SCons.Util.is_String(val): + self._dict[key] = [val] + else: + self._dict[key] = val + else: + try: + # Check if the original looks like a dictionary. + # If it is, we can't just try adding the value because + # dictionaries don't have __add__() methods, and + # things like UserList will incorrectly coerce the + # original dict to a list (which we don't want). + update_dict = orig.update + except AttributeError: + try: + # Most straightforward: just try to add them + # together. This will work in most cases, when the + # original and new values are of compatible types. + self._dict[key] = orig + val + except (KeyError, TypeError): + try: + # Check if the original is a list. + add_to_orig = orig.append + except AttributeError: + # The original isn't a list, but the new + # value is (by process of elimination), + # so insert the original in the new value + # (if there's one to insert) and replace + # the variable with it. + if orig: + val.insert(0, orig) + self._dict[key] = val + else: + # The original is a list, so append the new + # value to it (if there's a value to append). + if val: + add_to_orig(val) + else: + # The original looks like a dictionary, so update it + # based on what we think the value looks like. + if SCons.Util.is_List(val): + if key == 'CPPDEFINES': + orig = orig.items() + orig += val + self._dict[key] = orig + else: + for v in val: + orig[v] = None + else: + try: + update_dict(val) + except (AttributeError, TypeError, ValueError): + if SCons.Util.is_Dict(val): + for k, v in val.items(): + orig[k] = v + else: + orig[val] = None + self.scanner_map_delete(kw) + + # allow Dirs and strings beginning with # for top-relative + # Note this uses the current env's fs (in self). + def _canonicalize(self, path): + if not SCons.Util.is_String(path): # typically a Dir + path = str(path) + if path and path[0] == '#': + path = str(self.fs.Dir(path)) + return path + + def AppendENVPath(self, name, newpath, envname = 'ENV', + sep = os.pathsep, delete_existing=1): + """Append path elements to the path 'name' in the 'ENV' + dictionary for this environment. Will only add any particular + path once, and will normpath and normcase all paths to help + assure this. This can also handle the case where the env + variable is a list instead of a string. + + If delete_existing is 0, a newpath which is already in the path + will not be moved to the end (it will be left where it is). + """ + + orig = '' + if envname in self._dict and name in self._dict[envname]: + orig = self._dict[envname][name] + + nv = SCons.Util.AppendPath(orig, newpath, sep, delete_existing, + canonicalize=self._canonicalize) + + if envname not in self._dict: + self._dict[envname] = {} + + self._dict[envname][name] = nv + + def AppendUnique(self, delete_existing=0, **kw): + """Append values to existing construction variables + in an Environment, if they're not already there. + If delete_existing is 1, removes existing values first, so + values move to end. + """ + kw = copy_non_reserved_keywords(kw) + for key, val in kw.items(): + if SCons.Util.is_List(val): + val = _delete_duplicates(val, delete_existing) + if key not in self._dict or self._dict[key] in ('', None): + self._dict[key] = val + elif SCons.Util.is_Dict(self._dict[key]) and \ + SCons.Util.is_Dict(val): + self._dict[key].update(val) + elif SCons.Util.is_List(val): + dk = self._dict[key] + if key == 'CPPDEFINES': + tmp = [] + for i in val: + if SCons.Util.is_List(i): + if len(i) >= 2: + tmp.append((i[0], i[1])) + else: + tmp.append((i[0],)) + elif SCons.Util.is_Tuple(i): + tmp.append(i) + else: + tmp.append((i,)) + val = tmp + if SCons.Util.is_Dict(dk): + dk = dk.items() + elif SCons.Util.is_String(dk): + dk = [(dk,)] + else: + tmp = [] + for i in dk: + if SCons.Util.is_List(i): + if len(i) >= 2: + tmp.append((i[0], i[1])) + else: + tmp.append((i[0],)) + elif SCons.Util.is_Tuple(i): + tmp.append(i) + else: + tmp.append((i,)) + dk = tmp + else: + if not SCons.Util.is_List(dk): + dk = [dk] + if delete_existing: + dk = [x for x in dk if x not in val] + else: + val = [x for x in val if x not in dk] + self._dict[key] = dk + val + else: + dk = self._dict[key] + if SCons.Util.is_List(dk): + if key == 'CPPDEFINES': + tmp = [] + for i in dk: + if SCons.Util.is_List(i): + if len(i) >= 2: + tmp.append((i[0], i[1])) + else: + tmp.append((i[0],)) + elif SCons.Util.is_Tuple(i): + tmp.append(i) + else: + tmp.append((i,)) + dk = tmp + if SCons.Util.is_Dict(val): + val = val.items() + elif SCons.Util.is_String(val): + val = [(val,)] + if delete_existing: + dk = filter(lambda x, val=val: x not in val, dk) + self._dict[key] = dk + val + else: + dk = [x for x in dk if x not in val] + self._dict[key] = dk + val + else: + # By elimination, val is not a list. Since dk is a + # list, wrap val in a list first. + if delete_existing: + dk = filter(lambda x, val=val: x not in val, dk) + self._dict[key] = dk + [val] + else: + if not val in dk: + self._dict[key] = dk + [val] + else: + if key == 'CPPDEFINES': + if SCons.Util.is_String(dk): + dk = [dk] + elif SCons.Util.is_Dict(dk): + dk = dk.items() + if SCons.Util.is_String(val): + if val in dk: + val = [] + else: + val = [val] + elif SCons.Util.is_Dict(val): + tmp = [] + for i,j in val.iteritems(): + if j is not None: + tmp.append((i,j)) + else: + tmp.append(i) + val = tmp + if delete_existing: + dk = [x for x in dk if x not in val] + self._dict[key] = dk + val + self.scanner_map_delete(kw) + + def Clone(self, tools=[], toolpath=None, parse_flags = None, **kw): + """Return a copy of a construction Environment. The + copy is like a Python "deep copy"--that is, independent + copies are made recursively of each objects--except that + a reference is copied when an object is not deep-copyable + (like a function). There are no references to any mutable + objects in the original Environment. + """ + try: + builders = self._dict['BUILDERS'] + except KeyError: + pass + + clone = copy.copy(self) + # BUILDERS is not safe to do a simple copy + clone._dict = semi_deepcopy_dict(self._dict, ['BUILDERS']) + clone._dict['BUILDERS'] = BuilderDict(builders, clone) + + # Check the methods added via AddMethod() and re-bind them to + # the cloned environment. Only do this if the attribute hasn't + # been overwritten by the user explicitly and still points to + # the added method. + clone.added_methods = [] + for mw in self.added_methods: + if mw == getattr(self, mw.name): + clone.added_methods.append(mw.clone(clone)) + + clone._memo = {} + + # Apply passed-in variables before the tools + # so the tools can use the new variables + kw = copy_non_reserved_keywords(kw) + new = {} + for key, value in kw.items(): + new[key] = SCons.Subst.scons_subst_once(value, self, key) + clone.Replace(**new) + + apply_tools(clone, tools, toolpath) + + # apply them again in case the tools overwrote them + clone.Replace(**new) + + # Finally, apply any flags to be merged in + if parse_flags: clone.MergeFlags(parse_flags) + + if __debug__: logInstanceCreation(self, 'Environment.EnvironmentClone') + return clone + + def Copy(self, *args, **kw): + global _warn_copy_deprecated + if _warn_copy_deprecated: + msg = "The env.Copy() method is deprecated; use the env.Clone() method instead." + SCons.Warnings.warn(SCons.Warnings.DeprecatedCopyWarning, msg) + _warn_copy_deprecated = False + return self.Clone(*args, **kw) + + def _changed_build(self, dependency, target, prev_ni): + if dependency.changed_state(target, prev_ni): + return 1 + return self.decide_source(dependency, target, prev_ni) + + def _changed_content(self, dependency, target, prev_ni): + return dependency.changed_content(target, prev_ni) + + def _changed_source(self, dependency, target, prev_ni): + target_env = dependency.get_build_env() + type = target_env.get_tgt_sig_type() + if type == 'source': + return target_env.decide_source(dependency, target, prev_ni) + else: + return target_env.decide_target(dependency, target, prev_ni) + + def _changed_timestamp_then_content(self, dependency, target, prev_ni): + return dependency.changed_timestamp_then_content(target, prev_ni) + + def _changed_timestamp_newer(self, dependency, target, prev_ni): + return dependency.changed_timestamp_newer(target, prev_ni) + + def _changed_timestamp_match(self, dependency, target, prev_ni): + return dependency.changed_timestamp_match(target, prev_ni) + + def _copy_from_cache(self, src, dst): + return self.fs.copy(src, dst) + + def _copy2_from_cache(self, src, dst): + return self.fs.copy2(src, dst) + + def Decider(self, function): + copy_function = self._copy2_from_cache + if function in ('MD5', 'content'): + if not SCons.Util.md5: + raise UserError("MD5 signatures are not available in this version of Python.") + function = self._changed_content + elif function == 'MD5-timestamp': + function = self._changed_timestamp_then_content + elif function in ('timestamp-newer', 'make'): + function = self._changed_timestamp_newer + copy_function = self._copy_from_cache + elif function == 'timestamp-match': + function = self._changed_timestamp_match + elif not callable(function): + raise UserError("Unknown Decider value %s" % repr(function)) + + # We don't use AddMethod because we don't want to turn the + # function, which only expects three arguments, into a bound + # method, which would add self as an initial, fourth argument. + self.decide_target = function + self.decide_source = function + + self.copy_from_cache = copy_function + + def Detect(self, progs): + """Return the first available program in progs. + """ + if not SCons.Util.is_List(progs): + progs = [ progs ] + for prog in progs: + path = self.WhereIs(prog) + if path: return prog + return None + + def Dictionary(self, *args): + if not args: + return self._dict + dlist = [self._dict[x] for x in args] + if len(dlist) == 1: + dlist = dlist[0] + return dlist + + def Dump(self, key = None): + """ + Using the standard Python pretty printer, dump the contents of the + scons build environment to stdout. + + If the key passed in is anything other than None, then that will + be used as an index into the build environment dictionary and + whatever is found there will be fed into the pretty printer. Note + that this key is case sensitive. + """ + import pprint + pp = pprint.PrettyPrinter(indent=2) + if key: + dict = self.Dictionary(key) + else: + dict = self.Dictionary() + return pp.pformat(dict) + + def FindIxes(self, paths, prefix, suffix): + """ + Search a list of paths for something that matches the prefix and suffix. + + paths - the list of paths or nodes. + prefix - construction variable for the prefix. + suffix - construction variable for the suffix. + """ + + suffix = self.subst('$'+suffix) + prefix = self.subst('$'+prefix) + + for path in paths: + dir,name = os.path.split(str(path)) + if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix: + return path + + def ParseConfig(self, command, function=None, unique=1): + """ + Use the specified function to parse the output of the command + in order to modify the current environment. The 'command' can + be a string or a list of strings representing a command and + its arguments. 'Function' is an optional argument that takes + the environment, the output of the command, and the unique flag. + If no function is specified, MergeFlags, which treats the output + as the result of a typical 'X-config' command (i.e. gtk-config), + will merge the output into the appropriate variables. + """ + if function is None: + def parse_conf(env, cmd, unique=unique): + return env.MergeFlags(cmd, unique) + function = parse_conf + if SCons.Util.is_List(command): + command = ' '.join(command) + command = self.subst(command) + return function(self, self.backtick(command)) + + def ParseDepends(self, filename, must_exist=None, only_one=0): + """ + Parse a mkdep-style file for explicit dependencies. This is + completely abusable, and should be unnecessary in the "normal" + case of proper SCons configuration, but it may help make + the transition from a Make hierarchy easier for some people + to swallow. It can also be genuinely useful when using a tool + that can write a .d file, but for which writing a scanner would + be too complicated. + """ + filename = self.subst(filename) + try: + fp = open(filename, 'r') + except IOError: + if must_exist: + raise + return + lines = SCons.Util.LogicalLines(fp).readlines() + lines = [l for l in lines if l[0] != '#'] + tdlist = [] + for line in lines: + try: + target, depends = line.split(':', 1) + except (AttributeError, ValueError): + # Throws AttributeError if line isn't a string. Can throw + # ValueError if line doesn't split into two or more elements. + pass + else: + tdlist.append((target.split(), depends.split())) + if only_one: + targets = [] + for td in tdlist: + targets.extend(td[0]) + if len(targets) > 1: + raise SCons.Errors.UserError( + "More than one dependency target found in `%s': %s" + % (filename, targets)) + for target, depends in tdlist: + self.Depends(target, depends) + + def Platform(self, platform): + platform = self.subst(platform) + return SCons.Platform.Platform(platform)(self) + + def Prepend(self, **kw): + """Prepend values to existing construction variables + in an Environment. + """ + kw = copy_non_reserved_keywords(kw) + for key, val in kw.items(): + # It would be easier on the eyes to write this using + # "continue" statements whenever we finish processing an item, + # but Python 1.5.2 apparently doesn't let you use "continue" + # within try:-except: blocks, so we have to nest our code. + try: + orig = self._dict[key] + except KeyError: + # No existing variable in the environment, so just set + # it to the new value. + self._dict[key] = val + else: + try: + # Check if the original looks like a dictionary. + # If it is, we can't just try adding the value because + # dictionaries don't have __add__() methods, and + # things like UserList will incorrectly coerce the + # original dict to a list (which we don't want). + update_dict = orig.update + except AttributeError: + try: + # Most straightforward: just try to add them + # together. This will work in most cases, when the + # original and new values are of compatible types. + self._dict[key] = val + orig + except (KeyError, TypeError): + try: + # Check if the added value is a list. + add_to_val = val.append + except AttributeError: + # The added value isn't a list, but the + # original is (by process of elimination), + # so insert the the new value in the original + # (if there's one to insert). + if val: + orig.insert(0, val) + else: + # The added value is a list, so append + # the original to it (if there's a value + # to append). + if orig: + add_to_val(orig) + self._dict[key] = val + else: + # The original looks like a dictionary, so update it + # based on what we think the value looks like. + if SCons.Util.is_List(val): + for v in val: + orig[v] = None + else: + try: + update_dict(val) + except (AttributeError, TypeError, ValueError): + if SCons.Util.is_Dict(val): + for k, v in val.items(): + orig[k] = v + else: + orig[val] = None + self.scanner_map_delete(kw) + + def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep, + delete_existing=1): + """Prepend path elements to the path 'name' in the 'ENV' + dictionary for this environment. Will only add any particular + path once, and will normpath and normcase all paths to help + assure this. This can also handle the case where the env + variable is a list instead of a string. + + If delete_existing is 0, a newpath which is already in the path + will not be moved to the front (it will be left where it is). + """ + + orig = '' + if envname in self._dict and name in self._dict[envname]: + orig = self._dict[envname][name] + + nv = SCons.Util.PrependPath(orig, newpath, sep, delete_existing, + canonicalize=self._canonicalize) + + if envname not in self._dict: + self._dict[envname] = {} + + self._dict[envname][name] = nv + + def PrependUnique(self, delete_existing=0, **kw): + """Prepend values to existing construction variables + in an Environment, if they're not already there. + If delete_existing is 1, removes existing values first, so + values move to front. + """ + kw = copy_non_reserved_keywords(kw) + for key, val in kw.items(): + if SCons.Util.is_List(val): + val = _delete_duplicates(val, not delete_existing) + if key not in self._dict or self._dict[key] in ('', None): + self._dict[key] = val + elif SCons.Util.is_Dict(self._dict[key]) and \ + SCons.Util.is_Dict(val): + self._dict[key].update(val) + elif SCons.Util.is_List(val): + dk = self._dict[key] + if not SCons.Util.is_List(dk): + dk = [dk] + if delete_existing: + dk = [x for x in dk if x not in val] + else: + val = [x for x in val if x not in dk] + self._dict[key] = val + dk + else: + dk = self._dict[key] + if SCons.Util.is_List(dk): + # By elimination, val is not a list. Since dk is a + # list, wrap val in a list first. + if delete_existing: + dk = [x for x in dk if x not in val] + self._dict[key] = [val] + dk + else: + if not val in dk: + self._dict[key] = [val] + dk + else: + if delete_existing: + dk = [x for x in dk if x not in val] + self._dict[key] = val + dk + self.scanner_map_delete(kw) + + def Replace(self, **kw): + """Replace existing construction variables in an Environment + with new construction variables and/or values. + """ + try: + kwbd = kw['BUILDERS'] + except KeyError: + pass + else: + kwbd = BuilderDict(kwbd,self) + del kw['BUILDERS'] + self.__setitem__('BUILDERS', kwbd) + kw = copy_non_reserved_keywords(kw) + self._update(semi_deepcopy(kw)) + self.scanner_map_delete(kw) + + def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix): + """ + Replace old_prefix with new_prefix and old_suffix with new_suffix. + + env - Environment used to interpolate variables. + path - the path that will be modified. + old_prefix - construction variable for the old prefix. + old_suffix - construction variable for the old suffix. + new_prefix - construction variable for the new prefix. + new_suffix - construction variable for the new suffix. + """ + old_prefix = self.subst('$'+old_prefix) + old_suffix = self.subst('$'+old_suffix) + + new_prefix = self.subst('$'+new_prefix) + new_suffix = self.subst('$'+new_suffix) + + dir,name = os.path.split(str(path)) + if name[:len(old_prefix)] == old_prefix: + name = name[len(old_prefix):] + if name[-len(old_suffix):] == old_suffix: + name = name[:-len(old_suffix)] + return os.path.join(dir, new_prefix+name+new_suffix) + + def SetDefault(self, **kw): + for k in kw.keys(): + if k in self._dict: + del kw[k] + self.Replace(**kw) + + def _find_toolpath_dir(self, tp): + return self.fs.Dir(self.subst(tp)).srcnode().abspath + + def Tool(self, tool, toolpath=None, **kw): + if SCons.Util.is_String(tool): + tool = self.subst(tool) + if toolpath is None: + toolpath = self.get('toolpath', []) + toolpath = list(map(self._find_toolpath_dir, toolpath)) + tool = SCons.Tool.Tool(tool, toolpath, **kw) + tool(self) + + def WhereIs(self, prog, path=None, pathext=None, reject=[]): + """Find prog in the path. + """ + if path is None: + try: + path = self['ENV']['PATH'] + except KeyError: + pass + elif SCons.Util.is_String(path): + path = self.subst(path) + if pathext is None: + try: + pathext = self['ENV']['PATHEXT'] + except KeyError: + pass + elif SCons.Util.is_String(pathext): + pathext = self.subst(pathext) + prog = self.subst(prog) + path = SCons.Util.WhereIs(prog, path, pathext, reject) + if path: return path + return None + + ####################################################################### + # Public methods for doing real "SCons stuff" (manipulating + # dependencies, setting attributes on targets, etc.). These begin + # with upper-case letters. The essential characteristic of methods + # in this section is that they all *should* have corresponding + # same-named global functions. + ####################################################################### + + def Action(self, *args, **kw): + def subst_string(a, self=self): + if SCons.Util.is_String(a): + a = self.subst(a) + return a + nargs = list(map(subst_string, args)) + nkw = self.subst_kw(kw) + return SCons.Action.Action(*nargs, **nkw) + + def AddPreAction(self, files, action): + nodes = self.arg2nodes(files, self.fs.Entry) + action = SCons.Action.Action(action) + uniq = {} + for executor in [n.get_executor() for n in nodes]: + uniq[executor] = 1 + for executor in uniq.keys(): + executor.add_pre_action(action) + return nodes + + def AddPostAction(self, files, action): + nodes = self.arg2nodes(files, self.fs.Entry) + action = SCons.Action.Action(action) + uniq = {} + for executor in [n.get_executor() for n in nodes]: + uniq[executor] = 1 + for executor in uniq.keys(): + executor.add_post_action(action) + return nodes + + def Alias(self, target, source=[], action=None, **kw): + tlist = self.arg2nodes(target, self.ans.Alias) + if not SCons.Util.is_List(source): + source = [source] + source = [_f for _f in source if _f] + + if not action: + if not source: + # There are no source files and no action, so just + # return a target list of classic Alias Nodes, without + # any builder. The externally visible effect is that + # this will make the wrapping Script.BuildTask class + # say that there's "Nothing to be done" for this Alias, + # instead of that it's "up to date." + return tlist + + # No action, but there are sources. Re-call all the target + # builders to add the sources to each target. + result = [] + for t in tlist: + bld = t.get_builder(AliasBuilder) + result.extend(bld(self, t, source)) + return result + + nkw = self.subst_kw(kw) + nkw.update({ + 'action' : SCons.Action.Action(action), + 'source_factory' : self.fs.Entry, + 'multi' : 1, + 'is_explicit' : None, + }) + bld = SCons.Builder.Builder(**nkw) + + # Apply the Builder separately to each target so that the Aliases + # stay separate. If we did one "normal" Builder call with the + # whole target list, then all of the target Aliases would be + # associated under a single Executor. + result = [] + for t in tlist: + # Calling the convert() method will cause a new Executor to be + # created from scratch, so we have to explicitly initialize + # it with the target's existing sources, plus our new ones, + # so nothing gets lost. + b = t.get_builder() + if b is None or b is AliasBuilder: + b = bld + else: + nkw['action'] = b.action + action + b = SCons.Builder.Builder(**nkw) + t.convert() + result.extend(b(self, t, t.sources + source)) + return result + + def AlwaysBuild(self, *targets): + tlist = [] + for t in targets: + tlist.extend(self.arg2nodes(t, self.fs.Entry)) + for t in tlist: + t.set_always_build() + return tlist + + def BuildDir(self, *args, **kw): + msg = """BuildDir() and the build_dir keyword have been deprecated;\n\tuse VariantDir() and the variant_dir keyword instead.""" + SCons.Warnings.warn(SCons.Warnings.DeprecatedBuildDirWarning, msg) + if 'build_dir' in kw: + kw['variant_dir'] = kw['build_dir'] + del kw['build_dir'] + return self.VariantDir(*args, **kw) + + def Builder(self, **kw): + nkw = self.subst_kw(kw) + return SCons.Builder.Builder(**nkw) + + def CacheDir(self, path): + import SCons.CacheDir + if path is not None: + path = self.subst(path) + self._CacheDir_path = path + + def Clean(self, targets, files): + global CleanTargets + tlist = self.arg2nodes(targets, self.fs.Entry) + flist = self.arg2nodes(files, self.fs.Entry) + for t in tlist: + try: + CleanTargets[t].extend(flist) + except KeyError: + CleanTargets[t] = flist + + def Configure(self, *args, **kw): + nargs = [self] + if args: + nargs = nargs + self.subst_list(args)[0] + nkw = self.subst_kw(kw) + nkw['_depth'] = kw.get('_depth', 0) + 1 + try: + nkw['custom_tests'] = self.subst_kw(nkw['custom_tests']) + except KeyError: + pass + return SCons.SConf.SConf(*nargs, **nkw) + + def Command(self, target, source, action, **kw): + """Builds the supplied target files from the supplied + source files using the supplied action. Action may + be any type that the Builder constructor will accept + for an action.""" + bkw = { + 'action' : action, + 'target_factory' : self.fs.Entry, + 'source_factory' : self.fs.Entry, + } + try: bkw['source_scanner'] = kw['source_scanner'] + except KeyError: pass + else: del kw['source_scanner'] + bld = SCons.Builder.Builder(**bkw) + return bld(self, target, source, **kw) + + def Depends(self, target, dependency): + """Explicity specify that 'target's depend on 'dependency'.""" + tlist = self.arg2nodes(target, self.fs.Entry) + dlist = self.arg2nodes(dependency, self.fs.Entry) + for t in tlist: + t.add_dependency(dlist) + return tlist + + def Dir(self, name, *args, **kw): + """ + """ + s = self.subst(name) + if SCons.Util.is_Sequence(s): + result=[] + for e in s: + result.append(self.fs.Dir(e, *args, **kw)) + return result + return self.fs.Dir(s, *args, **kw) + + def NoClean(self, *targets): + """Tags a target so that it will not be cleaned by -c""" + tlist = [] + for t in targets: + tlist.extend(self.arg2nodes(t, self.fs.Entry)) + for t in tlist: + t.set_noclean() + return tlist + + def NoCache(self, *targets): + """Tags a target so that it will not be cached""" + tlist = [] + for t in targets: + tlist.extend(self.arg2nodes(t, self.fs.Entry)) + for t in tlist: + t.set_nocache() + return tlist + + def Entry(self, name, *args, **kw): + """ + """ + s = self.subst(name) + if SCons.Util.is_Sequence(s): + result=[] + for e in s: + result.append(self.fs.Entry(e, *args, **kw)) + return result + return self.fs.Entry(s, *args, **kw) + + def Environment(self, **kw): + return SCons.Environment.Environment(**self.subst_kw(kw)) + + def Execute(self, action, *args, **kw): + """Directly execute an action through an Environment + """ + action = self.Action(action, *args, **kw) + result = action([], [], self) + if isinstance(result, SCons.Errors.BuildError): + errstr = result.errstr + if result.filename: + errstr = result.filename + ': ' + errstr + sys.stderr.write("scons: *** %s\n" % errstr) + return result.status + else: + return result + + def File(self, name, *args, **kw): + """ + """ + s = self.subst(name) + if SCons.Util.is_Sequence(s): + result=[] + for e in s: + result.append(self.fs.File(e, *args, **kw)) + return result + return self.fs.File(s, *args, **kw) + + def FindFile(self, file, dirs): + file = self.subst(file) + nodes = self.arg2nodes(dirs, self.fs.Dir) + return SCons.Node.FS.find_file(file, tuple(nodes)) + + def Flatten(self, sequence): + return SCons.Util.flatten(sequence) + + def GetBuildPath(self, files): + result = list(map(str, self.arg2nodes(files, self.fs.Entry))) + if SCons.Util.is_List(files): + return result + else: + return result[0] + + def Glob(self, pattern, ondisk=True, source=False, strings=False): + return self.fs.Glob(self.subst(pattern), ondisk, source, strings) + + def Ignore(self, target, dependency): + """Ignore a dependency.""" + tlist = self.arg2nodes(target, self.fs.Entry) + dlist = self.arg2nodes(dependency, self.fs.Entry) + for t in tlist: + t.add_ignore(dlist) + return tlist + + def Literal(self, string): + return SCons.Subst.Literal(string) + + def Local(self, *targets): + ret = [] + for targ in targets: + if isinstance(targ, SCons.Node.Node): + targ.set_local() + ret.append(targ) + else: + for t in self.arg2nodes(targ, self.fs.Entry): + t.set_local() + ret.append(t) + return ret + + def Precious(self, *targets): + tlist = [] + for t in targets: + tlist.extend(self.arg2nodes(t, self.fs.Entry)) + for t in tlist: + t.set_precious() + return tlist + + def Repository(self, *dirs, **kw): + dirs = self.arg2nodes(list(dirs), self.fs.Dir) + self.fs.Repository(*dirs, **kw) + + def Requires(self, target, prerequisite): + """Specify that 'prerequisite' must be built before 'target', + (but 'target' does not actually depend on 'prerequisite' + and need not be rebuilt if it changes).""" + tlist = self.arg2nodes(target, self.fs.Entry) + plist = self.arg2nodes(prerequisite, self.fs.Entry) + for t in tlist: + t.add_prerequisite(plist) + return tlist + + def Scanner(self, *args, **kw): + nargs = [] + for arg in args: + if SCons.Util.is_String(arg): + arg = self.subst(arg) + nargs.append(arg) + nkw = self.subst_kw(kw) + return SCons.Scanner.Base(*nargs, **nkw) + + def SConsignFile(self, name=".sconsign", dbm_module=None): + if name is not None: + name = self.subst(name) + if not os.path.isabs(name): + name = os.path.join(str(self.fs.SConstruct_dir), name) + if name: + name = os.path.normpath(name) + sconsign_dir = os.path.dirname(name) + if sconsign_dir and not os.path.exists(sconsign_dir): + self.Execute(SCons.Defaults.Mkdir(sconsign_dir)) + SCons.SConsign.File(name, dbm_module) + + def SideEffect(self, side_effect, target): + """Tell scons that side_effects are built as side + effects of building targets.""" + side_effects = self.arg2nodes(side_effect, self.fs.Entry) + targets = self.arg2nodes(target, self.fs.Entry) + + for side_effect in side_effects: + if side_effect.multiple_side_effect_has_builder(): + raise SCons.Errors.UserError("Multiple ways to build the same target were specified for: %s" % str(side_effect)) + side_effect.add_source(targets) + side_effect.side_effect = 1 + self.Precious(side_effect) + for target in targets: + target.side_effects.append(side_effect) + return side_effects + + def SourceCode(self, entry, builder): + """Arrange for a source code builder for (part of) a tree.""" + msg = """SourceCode() has been deprecated and there is no replacement. +\tIf you need this function, please contact dev@scons.tigris.org.""" + SCons.Warnings.warn(SCons.Warnings.DeprecatedSourceCodeWarning, msg) + entries = self.arg2nodes(entry, self.fs.Entry) + for entry in entries: + entry.set_src_builder(builder) + return entries + + def SourceSignatures(self, type): + global _warn_source_signatures_deprecated + if _warn_source_signatures_deprecated: + msg = "The env.SourceSignatures() method is deprecated;\n" + \ + "\tconvert your build to use the env.Decider() method instead." + SCons.Warnings.warn(SCons.Warnings.DeprecatedSourceSignaturesWarning, msg) + _warn_source_signatures_deprecated = False + type = self.subst(type) + self.src_sig_type = type + if type == 'MD5': + if not SCons.Util.md5: + raise UserError("MD5 signatures are not available in this version of Python.") + self.decide_source = self._changed_content + elif type == 'timestamp': + self.decide_source = self._changed_timestamp_match + else: + raise UserError("Unknown source signature type '%s'" % type) + + def Split(self, arg): + """This function converts a string or list into a list of strings + or Nodes. This makes things easier for users by allowing files to + be specified as a white-space separated list to be split. + The input rules are: + - A single string containing names separated by spaces. These will be + split apart at the spaces. + - A single Node instance + - A list containing either strings or Node instances. Any strings + in the list are not split at spaces. + In all cases, the function returns a list of Nodes and strings.""" + if SCons.Util.is_List(arg): + return list(map(self.subst, arg)) + elif SCons.Util.is_String(arg): + return self.subst(arg).split() + else: + return [self.subst(arg)] + + def TargetSignatures(self, type): + global _warn_target_signatures_deprecated + if _warn_target_signatures_deprecated: + msg = "The env.TargetSignatures() method is deprecated;\n" + \ + "\tconvert your build to use the env.Decider() method instead." + SCons.Warnings.warn(SCons.Warnings.DeprecatedTargetSignaturesWarning, msg) + _warn_target_signatures_deprecated = False + type = self.subst(type) + self.tgt_sig_type = type + if type in ('MD5', 'content'): + if not SCons.Util.md5: + raise UserError("MD5 signatures are not available in this version of Python.") + self.decide_target = self._changed_content + elif type == 'timestamp': + self.decide_target = self._changed_timestamp_match + elif type == 'build': + self.decide_target = self._changed_build + elif type == 'source': + self.decide_target = self._changed_source + else: + raise UserError("Unknown target signature type '%s'"%type) + + def Value(self, value, built_value=None): + """ + """ + return SCons.Node.Python.Value(value, built_value) + + def VariantDir(self, variant_dir, src_dir, duplicate=1): + variant_dir = self.arg2nodes(variant_dir, self.fs.Dir)[0] + src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0] + self.fs.VariantDir(variant_dir, src_dir, duplicate) + + def FindSourceFiles(self, node='.'): + """ returns a list of all source files. + """ + node = self.arg2nodes(node, self.fs.Entry)[0] + + sources = [] + def build_source(ss): + for s in ss: + if isinstance(s, SCons.Node.FS.Dir): + build_source(s.all_children()) + elif s.has_builder(): + build_source(s.sources) + elif isinstance(s.disambiguate(), SCons.Node.FS.File): + sources.append(s) + build_source(node.all_children()) + + def final_source(node): + while (node != node.srcnode()): + node = node.srcnode() + return node + sources = map( final_source, sources ); + # remove duplicates + return list(set(sources)) + + def FindInstalledFiles(self): + """ returns the list of all targets of the Install and InstallAs Builder. + """ + from SCons.Tool import install + if install._UNIQUE_INSTALLED_FILES is None: + install._UNIQUE_INSTALLED_FILES = SCons.Util.uniquer_hashables(install._INSTALLED_FILES) + return install._UNIQUE_INSTALLED_FILES + + +class OverrideEnvironment(Base): + """A proxy that overrides variables in a wrapped construction + environment by returning values from an overrides dictionary in + preference to values from the underlying subject environment. + + This is a lightweight (I hope) proxy that passes through most use of + attributes to the underlying Environment.Base class, but has just + enough additional methods defined to act like a real construction + environment with overridden values. It can wrap either a Base + construction environment, or another OverrideEnvironment, which + can in turn nest arbitrary OverrideEnvironments... + + Note that we do *not* call the underlying base class + (SubsitutionEnvironment) initialization, because we get most of those + from proxying the attributes of the subject construction environment. + But because we subclass SubstitutionEnvironment, this class also + has inherited arg2nodes() and subst*() methods; those methods can't + be proxied because they need *this* object's methods to fetch the + values from the overrides dictionary. + """ + + def __init__(self, subject, overrides={}): + if __debug__: logInstanceCreation(self, 'Environment.OverrideEnvironment') + self.__dict__['__subject'] = subject + self.__dict__['overrides'] = overrides + + # Methods that make this class act like a proxy. + def __getattr__(self, name): + return getattr(self.__dict__['__subject'], name) + def __setattr__(self, name, value): + setattr(self.__dict__['__subject'], name, value) + + # Methods that make this class act like a dictionary. + def __getitem__(self, key): + try: + return self.__dict__['overrides'][key] + except KeyError: + return self.__dict__['__subject'].__getitem__(key) + def __setitem__(self, key, value): + if not is_valid_construction_var(key): + raise SCons.Errors.UserError("Illegal construction variable `%s'" % key) + self.__dict__['overrides'][key] = value + def __delitem__(self, key): + try: + del self.__dict__['overrides'][key] + except KeyError: + deleted = 0 + else: + deleted = 1 + try: + result = self.__dict__['__subject'].__delitem__(key) + except KeyError: + if not deleted: + raise + result = None + return result + def get(self, key, default=None): + """Emulates the get() method of dictionaries.""" + try: + return self.__dict__['overrides'][key] + except KeyError: + return self.__dict__['__subject'].get(key, default) + def has_key(self, key): + try: + self.__dict__['overrides'][key] + return 1 + except KeyError: + return key in self.__dict__['__subject'] + def __contains__(self, key): + if self.__dict__['overrides'].__contains__(key): + return 1 + return self.__dict__['__subject'].__contains__(key) + def Dictionary(self): + """Emulates the items() method of dictionaries.""" + d = self.__dict__['__subject'].Dictionary().copy() + d.update(self.__dict__['overrides']) + return d + def items(self): + """Emulates the items() method of dictionaries.""" + return list(self.Dictionary().items()) + + # Overridden private construction environment methods. + def _update(self, dict): + """Update an environment's values directly, bypassing the normal + checks that occur when users try to set items. + """ + self.__dict__['overrides'].update(dict) + + def gvars(self): + return self.__dict__['__subject'].gvars() + + def lvars(self): + lvars = self.__dict__['__subject'].lvars() + lvars.update(self.__dict__['overrides']) + return lvars + + # Overridden public construction environment methods. + def Replace(self, **kw): + kw = copy_non_reserved_keywords(kw) + self.__dict__['overrides'].update(semi_deepcopy(kw)) + +# The entry point that will be used by the external world +# to refer to a construction environment. This allows the wrapper +# interface to extend a construction environment for its own purposes +# by subclassing SCons.Environment.Base and then assigning the +# class to SCons.Environment.Environment. + +Environment = Base + +# An entry point for returning a proxy subclass instance that overrides +# the subst*() methods so they don't actually perform construction +# variable substitution. This is specifically intended to be the shim +# layer in between global function calls (which don't want construction +# variable substitution) and the DefaultEnvironment() (which would +# substitute variables if left to its own devices).""" +# +# We have to wrap this in a function that allows us to delay definition of +# the class until it's necessary, so that when it subclasses Environment +# it will pick up whatever Environment subclass the wrapper interface +# might have assigned to SCons.Environment.Environment. + +def NoSubstitutionProxy(subject): + class _NoSubstitutionProxy(Environment): + def __init__(self, subject): + self.__dict__['__subject'] = subject + def __getattr__(self, name): + return getattr(self.__dict__['__subject'], name) + def __setattr__(self, name, value): + return setattr(self.__dict__['__subject'], name, value) + def executor_to_lvars(self, kwdict): + if kwdict.has_key('executor'): + kwdict['lvars'] = kwdict['executor'].get_lvars() + del kwdict['executor'] + else: + kwdict['lvars'] = {} + def raw_to_mode(self, dict): + try: + raw = dict['raw'] + except KeyError: + pass + else: + del dict['raw'] + dict['mode'] = raw + def subst(self, string, *args, **kwargs): + return string + def subst_kw(self, kw, *args, **kwargs): + return kw + def subst_list(self, string, *args, **kwargs): + nargs = (string, self,) + args + nkw = kwargs.copy() + nkw['gvars'] = {} + self.executor_to_lvars(nkw) + self.raw_to_mode(nkw) + return SCons.Subst.scons_subst_list(*nargs, **nkw) + def subst_target_source(self, string, *args, **kwargs): + nargs = (string, self,) + args + nkw = kwargs.copy() + nkw['gvars'] = {} + self.executor_to_lvars(nkw) + self.raw_to_mode(nkw) + return SCons.Subst.scons_subst(*nargs, **nkw) + return _NoSubstitutionProxy(subject) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Errors.py b/scons/scons-local-2.3.0/SCons/Errors.py new file mode 100644 index 000000000..41dac1957 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Errors.py @@ -0,0 +1,205 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +"""SCons.Errors + +This file contains the exception classes used to handle internal +and user errors in SCons. + +""" + +__revision__ = "src/engine/SCons/Errors.py 2013/03/03 09:48:35 garyo" + +import SCons.Util + +import exceptions + +class BuildError(Exception): + """ Errors occuring while building. + + BuildError have the following attributes: + + Information about the cause of the build error: + ----------------------------------------------- + + errstr : a description of the error message + + status : the return code of the action that caused the build + error. Must be set to a non-zero value even if the + build error is not due to an action returning a + non-zero returned code. + + exitstatus : SCons exit status due to this build error. + Must be nonzero unless due to an explicit Exit() + call. Not always the same as status, since + actions return a status code that should be + respected, but SCons typically exits with 2 + irrespective of the return value of the failed + action. + + filename : The name of the file or directory that caused the + build error. Set to None if no files are associated with + this error. This might be different from the target + being built. For example, failure to create the + directory in which the target file will appear. It + can be None if the error is not due to a particular + filename. + + exc_info : Info about exception that caused the build + error. Set to (None, None, None) if this build + error is not due to an exception. + + + Information about the cause of the location of the error: + --------------------------------------------------------- + + node : the error occured while building this target node(s) + + executor : the executor that caused the build to fail (might + be None if the build failures is not due to the + executor failing) + + action : the action that caused the build to fail (might be + None if the build failures is not due to the an + action failure) + + command : the command line for the action that caused the + build to fail (might be None if the build failures + is not due to the an action failure) + """ + + def __init__(self, + node=None, errstr="Unknown error", status=2, exitstatus=2, + filename=None, executor=None, action=None, command=None, + exc_info=(None, None, None)): + + self.errstr = errstr + self.status = status + self.exitstatus = exitstatus + self.filename = filename + self.exc_info = exc_info + + self.node = node + self.executor = executor + self.action = action + self.command = command + + Exception.__init__(self, node, errstr, status, exitstatus, filename, + executor, action, command, exc_info) + + def __str__(self): + if self.filename: + return self.filename + ': ' + self.errstr + else: + return self.errstr + +class InternalError(Exception): + pass + +class UserError(Exception): + pass + +class StopError(Exception): + pass + +class EnvironmentError(Exception): + pass + +class MSVCError(IOError): + pass + +class ExplicitExit(Exception): + def __init__(self, node=None, status=None, *args): + self.node = node + self.status = status + self.exitstatus = status + Exception.__init__(self, *args) + +def convert_to_BuildError(status, exc_info=None): + """ + Convert any return code a BuildError Exception. + + `status' can either be a return code or an Exception. + The buildError.status we set here will normally be + used as the exit status of the "scons" process. + """ + if not exc_info and isinstance(status, Exception): + exc_info = (status.__class__, status, None) + + if isinstance(status, BuildError): + buildError = status + buildError.exitstatus = 2 # always exit with 2 on build errors + elif isinstance(status, ExplicitExit): + status = status.status + errstr = 'Explicit exit, status %s' % status + buildError = BuildError( + errstr=errstr, + status=status, # might be 0, OK here + exitstatus=status, # might be 0, OK here + exc_info=exc_info) + elif isinstance(status, (StopError, UserError)): + buildError = BuildError( + errstr=str(status), + status=2, + exitstatus=2, + exc_info=exc_info) + elif isinstance(status, exceptions.EnvironmentError): + # If an IOError/OSError happens, raise a BuildError. + # Report the name of the file or directory that caused the + # error, which might be different from the target being built + # (for example, failure to create the directory in which the + # target file will appear). + try: filename = status.filename + except AttributeError: filename = None + buildError = BuildError( + errstr=status.strerror, + status=status.errno, + exitstatus=2, + filename=filename, + exc_info=exc_info) + elif isinstance(status, Exception): + buildError = BuildError( + errstr='%s : %s' % (status.__class__.__name__, status), + status=2, + exitstatus=2, + exc_info=exc_info) + elif SCons.Util.is_String(status): + buildError = BuildError( + errstr=status, + status=2, + exitstatus=2) + else: + buildError = BuildError( + errstr="Error %s" % status, + status=status, + exitstatus=2) + + #import sys + #sys.stderr.write("convert_to_BuildError: status %s => (errstr %s, status %s)"%(status,buildError.errstr, buildError.status)) + return buildError + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Executor.py b/scons/scons-local-2.3.0/SCons/Executor.py new file mode 100644 index 000000000..0bea6fb69 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Executor.py @@ -0,0 +1,633 @@ +"""SCons.Executor + +A module for executing actions with specific lists of target and source +Nodes. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Executor.py 2013/03/03 09:48:35 garyo" + +import collections + +from SCons.Debug import logInstanceCreation +import SCons.Errors +import SCons.Memoize + + +class Batch(object): + """Remembers exact association between targets + and sources of executor.""" + def __init__(self, targets=[], sources=[]): + self.targets = targets + self.sources = sources + + + +class TSList(collections.UserList): + """A class that implements $TARGETS or $SOURCES expansions by wrapping + an executor Method. This class is used in the Executor.lvars() + to delay creation of NodeList objects until they're needed. + + Note that we subclass collections.UserList purely so that the + is_Sequence() function will identify an object of this class as + a list during variable expansion. We're not really using any + collections.UserList methods in practice. + """ + def __init__(self, func): + self.func = func + def __getattr__(self, attr): + nl = self.func() + return getattr(nl, attr) + def __getitem__(self, i): + nl = self.func() + return nl[i] + def __getslice__(self, i, j): + nl = self.func() + i = max(i, 0); j = max(j, 0) + return nl[i:j] + def __str__(self): + nl = self.func() + return str(nl) + def __repr__(self): + nl = self.func() + return repr(nl) + +class TSObject(object): + """A class that implements $TARGET or $SOURCE expansions by wrapping + an Executor method. + """ + def __init__(self, func): + self.func = func + def __getattr__(self, attr): + n = self.func() + return getattr(n, attr) + def __str__(self): + n = self.func() + if n: + return str(n) + return '' + def __repr__(self): + n = self.func() + if n: + return repr(n) + return '' + +def rfile(node): + """ + A function to return the results of a Node's rfile() method, + if it exists, and the Node itself otherwise (if it's a Value + Node, e.g.). + """ + try: + rfile = node.rfile + except AttributeError: + return node + else: + return rfile() + + +class Executor(object): + """A class for controlling instances of executing an action. + + This largely exists to hold a single association of an action, + environment, list of environment override dictionaries, targets + and sources for later processing as needed. + """ + + if SCons.Memoize.use_memoizer: + __metaclass__ = SCons.Memoize.Memoized_Metaclass + + memoizer_counters = [] + + def __init__(self, action, env=None, overridelist=[{}], + targets=[], sources=[], builder_kw={}): + if __debug__: logInstanceCreation(self, 'Executor.Executor') + self.set_action_list(action) + self.pre_actions = [] + self.post_actions = [] + self.env = env + self.overridelist = overridelist + if targets or sources: + self.batches = [Batch(targets[:], sources[:])] + else: + self.batches = [] + self.builder_kw = builder_kw + self._memo = {} + + def get_lvars(self): + try: + return self.lvars + except AttributeError: + self.lvars = { + 'CHANGED_SOURCES' : TSList(self._get_changed_sources), + 'CHANGED_TARGETS' : TSList(self._get_changed_targets), + 'SOURCE' : TSObject(self._get_source), + 'SOURCES' : TSList(self._get_sources), + 'TARGET' : TSObject(self._get_target), + 'TARGETS' : TSList(self._get_targets), + 'UNCHANGED_SOURCES' : TSList(self._get_unchanged_sources), + 'UNCHANGED_TARGETS' : TSList(self._get_unchanged_targets), + } + return self.lvars + + def _get_changes(self): + cs = [] + ct = [] + us = [] + ut = [] + for b in self.batches: + if b.targets[0].is_up_to_date(): + us.extend(list(map(rfile, b.sources))) + ut.extend(b.targets) + else: + cs.extend(list(map(rfile, b.sources))) + ct.extend(b.targets) + self._changed_sources_list = SCons.Util.NodeList(cs) + self._changed_targets_list = SCons.Util.NodeList(ct) + self._unchanged_sources_list = SCons.Util.NodeList(us) + self._unchanged_targets_list = SCons.Util.NodeList(ut) + + def _get_changed_sources(self, *args, **kw): + try: + return self._changed_sources_list + except AttributeError: + self._get_changes() + return self._changed_sources_list + + def _get_changed_targets(self, *args, **kw): + try: + return self._changed_targets_list + except AttributeError: + self._get_changes() + return self._changed_targets_list + + def _get_source(self, *args, **kw): + #return SCons.Util.NodeList([rfile(self.batches[0].sources[0]).get_subst_proxy()]) + return rfile(self.batches[0].sources[0]).get_subst_proxy() + + def _get_sources(self, *args, **kw): + return SCons.Util.NodeList([rfile(n).get_subst_proxy() for n in self.get_all_sources()]) + + def _get_target(self, *args, **kw): + #return SCons.Util.NodeList([self.batches[0].targets[0].get_subst_proxy()]) + return self.batches[0].targets[0].get_subst_proxy() + + def _get_targets(self, *args, **kw): + return SCons.Util.NodeList([n.get_subst_proxy() for n in self.get_all_targets()]) + + def _get_unchanged_sources(self, *args, **kw): + try: + return self._unchanged_sources_list + except AttributeError: + self._get_changes() + return self._unchanged_sources_list + + def _get_unchanged_targets(self, *args, **kw): + try: + return self._unchanged_targets_list + except AttributeError: + self._get_changes() + return self._unchanged_targets_list + + def get_action_targets(self): + if not self.action_list: + return [] + targets_string = self.action_list[0].get_targets(self.env, self) + if targets_string[0] == '$': + targets_string = targets_string[1:] + return self.get_lvars()[targets_string] + + def set_action_list(self, action): + import SCons.Util + if not SCons.Util.is_List(action): + if not action: + import SCons.Errors + raise SCons.Errors.UserError("Executor must have an action.") + action = [action] + self.action_list = action + + def get_action_list(self): + return self.pre_actions + self.action_list + self.post_actions + + def get_all_targets(self): + """Returns all targets for all batches of this Executor.""" + result = [] + for batch in self.batches: + result.extend(batch.targets) + return result + + def get_all_sources(self): + """Returns all sources for all batches of this Executor.""" + result = [] + for batch in self.batches: + result.extend(batch.sources) + return result + + def get_all_children(self): + """Returns all unique children (dependencies) for all batches + of this Executor. + + The Taskmaster can recognize when it's already evaluated a + Node, so we don't have to make this list unique for its intended + canonical use case, but we expect there to be a lot of redundancy + (long lists of batched .cc files #including the same .h files + over and over), so removing the duplicates once up front should + save the Taskmaster a lot of work. + """ + result = SCons.Util.UniqueList([]) + for target in self.get_all_targets(): + result.extend(target.children()) + return result + + def get_all_prerequisites(self): + """Returns all unique (order-only) prerequisites for all batches + of this Executor. + """ + result = SCons.Util.UniqueList([]) + for target in self.get_all_targets(): + result.extend(target.prerequisites) + return result + + def get_action_side_effects(self): + + """Returns all side effects for all batches of this + Executor used by the underlying Action. + """ + result = SCons.Util.UniqueList([]) + for target in self.get_action_targets(): + result.extend(target.side_effects) + return result + + memoizer_counters.append(SCons.Memoize.CountValue('get_build_env')) + + def get_build_env(self): + """Fetch or create the appropriate build Environment + for this Executor. + """ + try: + return self._memo['get_build_env'] + except KeyError: + pass + + # Create the build environment instance with appropriate + # overrides. These get evaluated against the current + # environment's construction variables so that users can + # add to existing values by referencing the variable in + # the expansion. + overrides = {} + for odict in self.overridelist: + overrides.update(odict) + + import SCons.Defaults + env = self.env or SCons.Defaults.DefaultEnvironment() + build_env = env.Override(overrides) + + self._memo['get_build_env'] = build_env + + return build_env + + def get_build_scanner_path(self, scanner): + """Fetch the scanner path for this executor's targets and sources. + """ + env = self.get_build_env() + try: + cwd = self.batches[0].targets[0].cwd + except (IndexError, AttributeError): + cwd = None + return scanner.path(env, cwd, + self.get_all_targets(), + self.get_all_sources()) + + def get_kw(self, kw={}): + result = self.builder_kw.copy() + result.update(kw) + result['executor'] = self + return result + + def do_nothing(self, target, kw): + return 0 + + def do_execute(self, target, kw): + """Actually execute the action list.""" + env = self.get_build_env() + kw = self.get_kw(kw) + status = 0 + for act in self.get_action_list(): + #args = (self.get_all_targets(), self.get_all_sources(), env) + args = ([], [], env) + status = act(*args, **kw) + if isinstance(status, SCons.Errors.BuildError): + status.executor = self + raise status + elif status: + msg = "Error %s" % status + raise SCons.Errors.BuildError( + errstr=msg, + node=self.batches[0].targets, + executor=self, + action=act) + return status + + # use extra indirection because with new-style objects (Python 2.2 + # and above) we can't override special methods, and nullify() needs + # to be able to do this. + + def __call__(self, target, **kw): + return self.do_execute(target, kw) + + def cleanup(self): + self._memo = {} + + def add_sources(self, sources): + """Add source files to this Executor's list. This is necessary + for "multi" Builders that can be called repeatedly to build up + a source file list for a given target.""" + # TODO(batch): extend to multiple batches + assert (len(self.batches) == 1) + # TODO(batch): remove duplicates? + sources = [x for x in sources if x not in self.batches[0].sources] + self.batches[0].sources.extend(sources) + + def get_sources(self): + return self.batches[0].sources + + def add_batch(self, targets, sources): + """Add pair of associated target and source to this Executor's list. + This is necessary for "batch" Builders that can be called repeatedly + to build up a list of matching target and source files that will be + used in order to update multiple target files at once from multiple + corresponding source files, for tools like MSVC that support it.""" + self.batches.append(Batch(targets, sources)) + + def prepare(self): + """ + Preparatory checks for whether this Executor can go ahead + and (try to) build its targets. + """ + for s in self.get_all_sources(): + if s.missing(): + msg = "Source `%s' not found, needed by target `%s'." + raise SCons.Errors.StopError(msg % (s, self.batches[0].targets[0])) + + def add_pre_action(self, action): + self.pre_actions.append(action) + + def add_post_action(self, action): + self.post_actions.append(action) + + # another extra indirection for new-style objects and nullify... + + def my_str(self): + env = self.get_build_env() + return "\n".join([action.genstring(self.get_all_targets(), + self.get_all_sources(), + env) + for action in self.get_action_list()]) + + + def __str__(self): + return self.my_str() + + def nullify(self): + self.cleanup() + self.do_execute = self.do_nothing + self.my_str = lambda: '' + + memoizer_counters.append(SCons.Memoize.CountValue('get_contents')) + + def get_contents(self): + """Fetch the signature contents. This is the main reason this + class exists, so we can compute this once and cache it regardless + of how many target or source Nodes there are. + """ + try: + return self._memo['get_contents'] + except KeyError: + pass + env = self.get_build_env() + result = "".join([action.get_contents(self.get_all_targets(), + self.get_all_sources(), + env) + for action in self.get_action_list()]) + self._memo['get_contents'] = result + return result + + def get_timestamp(self): + """Fetch a time stamp for this Executor. We don't have one, of + course (only files do), but this is the interface used by the + timestamp module. + """ + return 0 + + def scan_targets(self, scanner): + # TODO(batch): scan by batches + self.scan(scanner, self.get_all_targets()) + + def scan_sources(self, scanner): + # TODO(batch): scan by batches + if self.batches[0].sources: + self.scan(scanner, self.get_all_sources()) + + def scan(self, scanner, node_list): + """Scan a list of this Executor's files (targets or sources) for + implicit dependencies and update all of the targets with them. + This essentially short-circuits an N*M scan of the sources for + each individual target, which is a hell of a lot more efficient. + """ + env = self.get_build_env() + + # TODO(batch): scan by batches) + deps = [] + if scanner: + for node in node_list: + node.disambiguate() + s = scanner.select(node) + if not s: + continue + path = self.get_build_scanner_path(s) + deps.extend(node.get_implicit_deps(env, s, path)) + else: + kw = self.get_kw() + for node in node_list: + node.disambiguate() + scanner = node.get_env_scanner(env, kw) + if not scanner: + continue + scanner = scanner.select(node) + if not scanner: + continue + path = self.get_build_scanner_path(scanner) + deps.extend(node.get_implicit_deps(env, scanner, path)) + + deps.extend(self.get_implicit_deps()) + + for tgt in self.get_all_targets(): + tgt.add_to_implicit(deps) + + def _get_unignored_sources_key(self, node, ignore=()): + return (node,) + tuple(ignore) + + memoizer_counters.append(SCons.Memoize.CountDict('get_unignored_sources', _get_unignored_sources_key)) + + def get_unignored_sources(self, node, ignore=()): + key = (node,) + tuple(ignore) + try: + memo_dict = self._memo['get_unignored_sources'] + except KeyError: + memo_dict = {} + self._memo['get_unignored_sources'] = memo_dict + else: + try: + return memo_dict[key] + except KeyError: + pass + + if node: + # TODO: better way to do this (it's a linear search, + # but it may not be critical path)? + sourcelist = [] + for b in self.batches: + if node in b.targets: + sourcelist = b.sources + break + else: + sourcelist = self.get_all_sources() + if ignore: + idict = {} + for i in ignore: + idict[i] = 1 + sourcelist = [s for s in sourcelist if s not in idict] + + memo_dict[key] = sourcelist + + return sourcelist + + def get_implicit_deps(self): + """Return the executor's implicit dependencies, i.e. the nodes of + the commands to be executed.""" + result = [] + build_env = self.get_build_env() + for act in self.get_action_list(): + deps = act.get_implicit_deps(self.get_all_targets(), + self.get_all_sources(), + build_env) + result.extend(deps) + return result + + + +_batch_executors = {} + +def GetBatchExecutor(key): + return _batch_executors[key] + +def AddBatchExecutor(key, executor): + assert key not in _batch_executors + _batch_executors[key] = executor + +nullenv = None + + +def get_NullEnvironment(): + """Use singleton pattern for Null Environments.""" + global nullenv + + import SCons.Util + class NullEnvironment(SCons.Util.Null): + import SCons.CacheDir + _CacheDir_path = None + _CacheDir = SCons.CacheDir.CacheDir(None) + def get_CacheDir(self): + return self._CacheDir + + if not nullenv: + nullenv = NullEnvironment() + return nullenv + +class Null(object): + """A null Executor, with a null build Environment, that does + nothing when the rest of the methods call it. + + This might be able to disapper when we refactor things to + disassociate Builders from Nodes entirely, so we're not + going to worry about unit tests for this--at least for now. + """ + def __init__(self, *args, **kw): + if __debug__: logInstanceCreation(self, 'Executor.Null') + self.batches = [Batch(kw['targets'][:], [])] + def get_build_env(self): + return get_NullEnvironment() + def get_build_scanner_path(self): + return None + def cleanup(self): + pass + def prepare(self): + pass + def get_unignored_sources(self, *args, **kw): + return tuple(()) + def get_action_targets(self): + return [] + def get_action_list(self): + return [] + def get_all_targets(self): + return self.batches[0].targets + def get_all_sources(self): + return self.batches[0].targets[0].sources + def get_all_children(self): + return self.batches[0].targets[0].children() + def get_all_prerequisites(self): + return [] + def get_action_side_effects(self): + return [] + def __call__(self, *args, **kw): + return 0 + def get_contents(self): + return '' + def _morph(self): + """Morph this Null executor to a real Executor object.""" + batches = self.batches + self.__class__ = Executor + self.__init__([]) + self.batches = batches + + # The following methods require morphing this Null Executor to a + # real Executor object. + + def add_pre_action(self, action): + self._morph() + self.add_pre_action(action) + def add_post_action(self, action): + self._morph() + self.add_post_action(action) + def set_action_list(self, action): + self._morph() + self.set_action_list(action) + + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Job.py b/scons/scons-local-2.3.0/SCons/Job.py new file mode 100644 index 000000000..4e51b993a --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Job.py @@ -0,0 +1,435 @@ +"""SCons.Job + +This module defines the Serial and Parallel classes that execute tasks to +complete a build. The Jobs class provides a higher level interface to start, +stop, and wait on jobs. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Job.py 2013/03/03 09:48:35 garyo" + +import SCons.compat + +import os +import signal + +import SCons.Errors + +# The default stack size (in kilobytes) of the threads used to execute +# jobs in parallel. +# +# We use a stack size of 256 kilobytes. The default on some platforms +# is too large and prevents us from creating enough threads to fully +# parallelized the build. For example, the default stack size on linux +# is 8 MBytes. + +explicit_stack_size = None +default_stack_size = 256 + +interrupt_msg = 'Build interrupted.' + + +class InterruptState(object): + def __init__(self): + self.interrupted = False + + def set(self): + self.interrupted = True + + def __call__(self): + return self.interrupted + + +class Jobs(object): + """An instance of this class initializes N jobs, and provides + methods for starting, stopping, and waiting on all N jobs. + """ + + def __init__(self, num, taskmaster): + """ + create 'num' jobs using the given taskmaster. + + If 'num' is 1 or less, then a serial job will be used, + otherwise a parallel job with 'num' worker threads will + be used. + + The 'num_jobs' attribute will be set to the actual number of jobs + allocated. If more than one job is requested but the Parallel + class can't do it, it gets reset to 1. Wrapping interfaces that + care should check the value of 'num_jobs' after initialization. + """ + + self.job = None + if num > 1: + stack_size = explicit_stack_size + if stack_size is None: + stack_size = default_stack_size + + try: + self.job = Parallel(taskmaster, num, stack_size) + self.num_jobs = num + except NameError: + pass + if self.job is None: + self.job = Serial(taskmaster) + self.num_jobs = 1 + + def run(self, postfunc=lambda: None): + """Run the jobs. + + postfunc() will be invoked after the jobs has run. It will be + invoked even if the jobs are interrupted by a keyboard + interrupt (well, in fact by a signal such as either SIGINT, + SIGTERM or SIGHUP). The execution of postfunc() is protected + against keyboard interrupts and is guaranteed to run to + completion.""" + self._setup_sig_handler() + try: + self.job.start() + finally: + postfunc() + self._reset_sig_handler() + + def were_interrupted(self): + """Returns whether the jobs were interrupted by a signal.""" + return self.job.interrupted() + + def _setup_sig_handler(self): + """Setup an interrupt handler so that SCons can shutdown cleanly in + various conditions: + + a) SIGINT: Keyboard interrupt + b) SIGTERM: kill or system shutdown + c) SIGHUP: Controlling shell exiting + + We handle all of these cases by stopping the taskmaster. It + turns out that it very difficult to stop the build process + by throwing asynchronously an exception such as + KeyboardInterrupt. For example, the python Condition + variables (threading.Condition) and queue's do not seem to + asynchronous-exception-safe. It would require adding a whole + bunch of try/finally block and except KeyboardInterrupt all + over the place. + + Note also that we have to be careful to handle the case when + SCons forks before executing another process. In that case, we + want the child to exit immediately. + """ + def handler(signum, stack, self=self, parentpid=os.getpid()): + if os.getpid() == parentpid: + self.job.taskmaster.stop() + self.job.interrupted.set() + else: + os._exit(2) + + self.old_sigint = signal.signal(signal.SIGINT, handler) + self.old_sigterm = signal.signal(signal.SIGTERM, handler) + try: + self.old_sighup = signal.signal(signal.SIGHUP, handler) + except AttributeError: + pass + + def _reset_sig_handler(self): + """Restore the signal handlers to their previous state (before the + call to _setup_sig_handler().""" + + signal.signal(signal.SIGINT, self.old_sigint) + signal.signal(signal.SIGTERM, self.old_sigterm) + try: + signal.signal(signal.SIGHUP, self.old_sighup) + except AttributeError: + pass + +class Serial(object): + """This class is used to execute tasks in series, and is more efficient + than Parallel, but is only appropriate for non-parallel builds. Only + one instance of this class should be in existence at a time. + + This class is not thread safe. + """ + + def __init__(self, taskmaster): + """Create a new serial job given a taskmaster. + + The taskmaster's next_task() method should return the next task + that needs to be executed, or None if there are no more tasks. The + taskmaster's executed() method will be called for each task when it + is successfully executed or failed() will be called if it failed to + execute (e.g. execute() raised an exception).""" + + self.taskmaster = taskmaster + self.interrupted = InterruptState() + + def start(self): + """Start the job. This will begin pulling tasks from the taskmaster + and executing them, and return when there are no more tasks. If a task + fails to execute (i.e. execute() raises an exception), then the job will + stop.""" + + while True: + task = self.taskmaster.next_task() + + if task is None: + break + + try: + task.prepare() + if task.needs_execute(): + task.execute() + except: + if self.interrupted(): + try: + raise SCons.Errors.BuildError( + task.targets[0], errstr=interrupt_msg) + except: + task.exception_set() + else: + task.exception_set() + + # Let the failed() callback function arrange for the + # build to stop if that's appropriate. + task.failed() + else: + task.executed() + + task.postprocess() + self.taskmaster.cleanup() + + +# Trap import failure so that everything in the Job module but the +# Parallel class (and its dependent classes) will work if the interpreter +# doesn't support threads. +try: + import queue + import threading +except ImportError: + pass +else: + class Worker(threading.Thread): + """A worker thread waits on a task to be posted to its request queue, + dequeues the task, executes it, and posts a tuple including the task + and a boolean indicating whether the task executed successfully. """ + + def __init__(self, requestQueue, resultsQueue, interrupted): + threading.Thread.__init__(self) + self.setDaemon(1) + self.requestQueue = requestQueue + self.resultsQueue = resultsQueue + self.interrupted = interrupted + self.start() + + def run(self): + while True: + task = self.requestQueue.get() + + if task is None: + # The "None" value is used as a sentinel by + # ThreadPool.cleanup(). This indicates that there + # are no more tasks, so we should quit. + break + + try: + if self.interrupted(): + raise SCons.Errors.BuildError( + task.targets[0], errstr=interrupt_msg) + task.execute() + except: + task.exception_set() + ok = False + else: + ok = True + + self.resultsQueue.put((task, ok)) + + class ThreadPool(object): + """This class is responsible for spawning and managing worker threads.""" + + def __init__(self, num, stack_size, interrupted): + """Create the request and reply queues, and 'num' worker threads. + + One must specify the stack size of the worker threads. The + stack size is specified in kilobytes. + """ + self.requestQueue = queue.Queue(0) + self.resultsQueue = queue.Queue(0) + + try: + prev_size = threading.stack_size(stack_size*1024) + except AttributeError, e: + # Only print a warning if the stack size has been + # explicitly set. + if not explicit_stack_size is None: + msg = "Setting stack size is unsupported by this version of Python:\n " + \ + e.args[0] + SCons.Warnings.warn(SCons.Warnings.StackSizeWarning, msg) + except ValueError, e: + msg = "Setting stack size failed:\n " + str(e) + SCons.Warnings.warn(SCons.Warnings.StackSizeWarning, msg) + + # Create worker threads + self.workers = [] + for _ in range(num): + worker = Worker(self.requestQueue, self.resultsQueue, interrupted) + self.workers.append(worker) + + if 'prev_size' in locals(): + threading.stack_size(prev_size) + + def put(self, task): + """Put task into request queue.""" + self.requestQueue.put(task) + + def get(self): + """Remove and return a result tuple from the results queue.""" + return self.resultsQueue.get() + + def preparation_failed(self, task): + self.resultsQueue.put((task, False)) + + def cleanup(self): + """ + Shuts down the thread pool, giving each worker thread a + chance to shut down gracefully. + """ + # For each worker thread, put a sentinel "None" value + # on the requestQueue (indicating that there's no work + # to be done) so that each worker thread will get one and + # terminate gracefully. + for _ in self.workers: + self.requestQueue.put(None) + + # Wait for all of the workers to terminate. + # + # If we don't do this, later Python versions (2.4, 2.5) often + # seem to raise exceptions during shutdown. This happens + # in requestQueue.get(), as an assertion failure that + # requestQueue.not_full is notified while not acquired, + # seemingly because the main thread has shut down (or is + # in the process of doing so) while the workers are still + # trying to pull sentinels off the requestQueue. + # + # Normally these terminations should happen fairly quickly, + # but we'll stick a one-second timeout on here just in case + # someone gets hung. + for worker in self.workers: + worker.join(1.0) + self.workers = [] + + class Parallel(object): + """This class is used to execute tasks in parallel, and is somewhat + less efficient than Serial, but is appropriate for parallel builds. + + This class is thread safe. + """ + + def __init__(self, taskmaster, num, stack_size): + """Create a new parallel job given a taskmaster. + + The taskmaster's next_task() method should return the next + task that needs to be executed, or None if there are no more + tasks. The taskmaster's executed() method will be called + for each task when it is successfully executed or failed() + will be called if the task failed to execute (i.e. execute() + raised an exception). + + Note: calls to taskmaster are serialized, but calls to + execute() on distinct tasks are not serialized, because + that is the whole point of parallel jobs: they can execute + multiple tasks simultaneously. """ + + self.taskmaster = taskmaster + self.interrupted = InterruptState() + self.tp = ThreadPool(num, stack_size, self.interrupted) + + self.maxjobs = num + + def start(self): + """Start the job. This will begin pulling tasks from the + taskmaster and executing them, and return when there are no + more tasks. If a task fails to execute (i.e. execute() raises + an exception), then the job will stop.""" + + jobs = 0 + + while True: + # Start up as many available tasks as we're + # allowed to. + while jobs < self.maxjobs: + task = self.taskmaster.next_task() + if task is None: + break + + try: + # prepare task for execution + task.prepare() + except: + task.exception_set() + task.failed() + task.postprocess() + else: + if task.needs_execute(): + # dispatch task + self.tp.put(task) + jobs = jobs + 1 + else: + task.executed() + task.postprocess() + + if not task and not jobs: break + + # Let any/all completed tasks finish up before we go + # back and put the next batch of tasks on the queue. + while True: + task, ok = self.tp.get() + jobs = jobs - 1 + + if ok: + task.executed() + else: + if self.interrupted(): + try: + raise SCons.Errors.BuildError( + task.targets[0], errstr=interrupt_msg) + except: + task.exception_set() + + # Let the failed() callback function arrange + # for the build to stop if that's appropriate. + task.failed() + + task.postprocess() + + if self.tp.resultsQueue.empty(): + break + + self.tp.cleanup() + self.taskmaster.cleanup() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Memoize.py b/scons/scons-local-2.3.0/SCons/Memoize.py new file mode 100644 index 000000000..af84745e1 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Memoize.py @@ -0,0 +1,244 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Memoize.py 2013/03/03 09:48:35 garyo" + +__doc__ = """Memoizer + +A metaclass implementation to count hits and misses of the computed +values that various methods cache in memory. + +Use of this modules assumes that wrapped methods be coded to cache their +values in a consistent way. Here is an example of wrapping a method +that returns a computed value, with no input parameters: + + memoizer_counters = [] # Memoization + + memoizer_counters.append(SCons.Memoize.CountValue('foo')) # Memoization + + def foo(self): + + try: # Memoization + return self._memo['foo'] # Memoization + except KeyError: # Memoization + pass # Memoization + + result = self.compute_foo_value() + + self._memo['foo'] = result # Memoization + + return result + +Here is an example of wrapping a method that will return different values +based on one or more input arguments: + + def _bar_key(self, argument): # Memoization + return argument # Memoization + + memoizer_counters.append(SCons.Memoize.CountDict('bar', _bar_key)) # Memoization + + def bar(self, argument): + + memo_key = argument # Memoization + try: # Memoization + memo_dict = self._memo['bar'] # Memoization + except KeyError: # Memoization + memo_dict = {} # Memoization + self._memo['dict'] = memo_dict # Memoization + else: # Memoization + try: # Memoization + return memo_dict[memo_key] # Memoization + except KeyError: # Memoization + pass # Memoization + + result = self.compute_bar_value(argument) + + memo_dict[memo_key] = result # Memoization + + return result + +At one point we avoided replicating this sort of logic in all the methods +by putting it right into this module, but we've moved away from that at +present (see the "Historical Note," below.). + +Deciding what to cache is tricky, because different configurations +can have radically different performance tradeoffs, and because the +tradeoffs involved are often so non-obvious. Consequently, deciding +whether or not to cache a given method will likely be more of an art than +a science, but should still be based on available data from this module. +Here are some VERY GENERAL guidelines about deciding whether or not to +cache return values from a method that's being called a lot: + + -- The first question to ask is, "Can we change the calling code + so this method isn't called so often?" Sometimes this can be + done by changing the algorithm. Sometimes the *caller* should + be memoized, not the method you're looking at. + + -- The memoized function should be timed with multiple configurations + to make sure it doesn't inadvertently slow down some other + configuration. + + -- When memoizing values based on a dictionary key composed of + input arguments, you don't need to use all of the arguments + if some of them don't affect the return values. + +Historical Note: The initial Memoizer implementation actually handled +the caching of values for the wrapped methods, based on a set of generic +algorithms for computing hashable values based on the method's arguments. +This collected caching logic nicely, but had two drawbacks: + + Running arguments through a generic key-conversion mechanism is slower + (and less flexible) than just coding these things directly. Since the + methods that need memoized values are generally performance-critical, + slowing them down in order to collect the logic isn't the right + tradeoff. + + Use of the memoizer really obscured what was being called, because + all the memoized methods were wrapped with re-used generic methods. + This made it more difficult, for example, to use the Python profiler + to figure out how to optimize the underlying methods. +""" + +import types + +# A flag controlling whether or not we actually use memoization. +use_memoizer = None + +CounterList = [] + +class Counter(object): + """ + Base class for counting memoization hits and misses. + + We expect that the metaclass initialization will have filled in + the .name attribute that represents the name of the function + being counted. + """ + def __init__(self, method_name): + """ + """ + self.method_name = method_name + self.hit = 0 + self.miss = 0 + CounterList.append(self) + def display(self): + fmt = " %7d hits %7d misses %s()" + print fmt % (self.hit, self.miss, self.name) + def __cmp__(self, other): + try: + return cmp(self.name, other.name) + except AttributeError: + return 0 + +class CountValue(Counter): + """ + A counter class for simple, atomic memoized values. + + A CountValue object should be instantiated in a class for each of + the class's methods that memoizes its return value by simply storing + the return value in its _memo dictionary. + + We expect that the metaclass initialization will fill in the + .underlying_method attribute with the method that we're wrapping. + We then call the underlying_method method after counting whether + its memoized value has already been set (a hit) or not (a miss). + """ + def __call__(self, *args, **kw): + obj = args[0] + if self.method_name in obj._memo: + self.hit = self.hit + 1 + else: + self.miss = self.miss + 1 + return self.underlying_method(*args, **kw) + +class CountDict(Counter): + """ + A counter class for memoized values stored in a dictionary, with + keys based on the method's input arguments. + + A CountDict object is instantiated in a class for each of the + class's methods that memoizes its return value in a dictionary, + indexed by some key that can be computed from one or more of + its input arguments. + + We expect that the metaclass initialization will fill in the + .underlying_method attribute with the method that we're wrapping. + We then call the underlying_method method after counting whether the + computed key value is already present in the memoization dictionary + (a hit) or not (a miss). + """ + def __init__(self, method_name, keymaker): + """ + """ + Counter.__init__(self, method_name) + self.keymaker = keymaker + def __call__(self, *args, **kw): + obj = args[0] + try: + memo_dict = obj._memo[self.method_name] + except KeyError: + self.miss = self.miss + 1 + else: + key = self.keymaker(*args, **kw) + if key in memo_dict: + self.hit = self.hit + 1 + else: + self.miss = self.miss + 1 + return self.underlying_method(*args, **kw) + +class Memoizer(object): + """Object which performs caching of method calls for its 'primary' + instance.""" + + def __init__(self): + pass + +def Dump(title=None): + if title: + print title + CounterList.sort() + for counter in CounterList: + counter.display() + +class Memoized_Metaclass(type): + def __init__(cls, name, bases, cls_dict): + super(Memoized_Metaclass, cls).__init__(name, bases, cls_dict) + + for counter in cls_dict.get('memoizer_counters', []): + method_name = counter.method_name + + counter.name = cls.__name__ + '.' + method_name + counter.underlying_method = cls_dict[method_name] + + replacement_method = types.MethodType(counter, None, cls) + setattr(cls, method_name, replacement_method) + +def EnableMemoization(): + global use_memoizer + use_memoizer = 1 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Node/Alias.py b/scons/scons-local-2.3.0/SCons/Node/Alias.py new file mode 100644 index 000000000..02fe12120 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Node/Alias.py @@ -0,0 +1,152 @@ + +"""scons.Node.Alias + +Alias nodes. + +This creates a hash of global Aliases (dummy targets). + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Node/Alias.py 2013/03/03 09:48:35 garyo" + +import collections + +import SCons.Errors +import SCons.Node +import SCons.Util + +class AliasNameSpace(collections.UserDict): + def Alias(self, name, **kw): + if isinstance(name, SCons.Node.Alias.Alias): + return name + try: + a = self[name] + except KeyError: + a = SCons.Node.Alias.Alias(name, **kw) + self[name] = a + return a + + def lookup(self, name, **kw): + try: + return self[name] + except KeyError: + return None + +class AliasNodeInfo(SCons.Node.NodeInfoBase): + current_version_id = 1 + field_list = ['csig'] + def str_to_node(self, s): + return default_ans.Alias(s) + +class AliasBuildInfo(SCons.Node.BuildInfoBase): + current_version_id = 1 + +class Alias(SCons.Node.Node): + + NodeInfo = AliasNodeInfo + BuildInfo = AliasBuildInfo + + def __init__(self, name): + SCons.Node.Node.__init__(self) + self.name = name + + def str_for_display(self): + return '"' + self.__str__() + '"' + + def __str__(self): + return self.name + + def make_ready(self): + self.get_csig() + + really_build = SCons.Node.Node.build + is_up_to_date = SCons.Node.Node.children_are_up_to_date + + def is_under(self, dir): + # Make Alias nodes get built regardless of + # what directory scons was run from. Alias nodes + # are outside the filesystem: + return 1 + + def get_contents(self): + """The contents of an alias is the concatenation + of the content signatures of all its sources.""" + childsigs = [n.get_csig() for n in self.children()] + return ''.join(childsigs) + + def sconsign(self): + """An Alias is not recorded in .sconsign files""" + pass + + # + # + # + + def changed_since_last_build(self, target, prev_ni): + cur_csig = self.get_csig() + try: + return cur_csig != prev_ni.csig + except AttributeError: + return 1 + + def build(self): + """A "builder" for aliases.""" + pass + + def convert(self): + try: del self.builder + except AttributeError: pass + self.reset_executor() + self.build = self.really_build + + def get_csig(self): + """ + Generate a node's content signature, the digested signature + of its content. + + node - the node + cache - alternate node to use for the signature cache + returns - the content signature + """ + try: + return self.ninfo.csig + except AttributeError: + pass + + contents = self.get_contents() + csig = SCons.Util.MD5signature(contents) + self.get_ninfo().csig = csig + return csig + +default_ans = AliasNameSpace() + +SCons.Node.arg2nodes_lookups.append(default_ans.lookup) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Node/FS.py b/scons/scons-local-2.3.0/SCons/Node/FS.py new file mode 100644 index 000000000..a21561f2e --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Node/FS.py @@ -0,0 +1,3302 @@ +"""scons.Node.FS + +File system nodes. + +These Nodes represent the canonical external objects that people think +of when they think of building software: files and directories. + +This holds a "default_fs" variable that should be initialized with an FS +that can be used by scripts or modules looking for the canonical default. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Node/FS.py 2013/03/03 09:48:35 garyo" + +import fnmatch +import os +import re +import shutil +import stat +import sys +import time +import codecs + +import SCons.Action +from SCons.Debug import logInstanceCreation +import SCons.Errors +import SCons.Memoize +import SCons.Node +import SCons.Node.Alias +import SCons.Subst +import SCons.Util +import SCons.Warnings + +from SCons.Debug import Trace + +do_store_info = True +print_duplicate = 0 + + +class EntryProxyAttributeError(AttributeError): + """ + An AttributeError subclass for recording and displaying the name + of the underlying Entry involved in an AttributeError exception. + """ + def __init__(self, entry_proxy, attribute): + AttributeError.__init__(self) + self.entry_proxy = entry_proxy + self.attribute = attribute + def __str__(self): + entry = self.entry_proxy.get() + fmt = "%s instance %s has no attribute %s" + return fmt % (entry.__class__.__name__, + repr(entry.name), + repr(self.attribute)) + +# The max_drift value: by default, use a cached signature value for +# any file that's been untouched for more than two days. +default_max_drift = 2*24*60*60 + +# +# We stringify these file system Nodes a lot. Turning a file system Node +# into a string is non-trivial, because the final string representation +# can depend on a lot of factors: whether it's a derived target or not, +# whether it's linked to a repository or source directory, and whether +# there's duplication going on. The normal technique for optimizing +# calculations like this is to memoize (cache) the string value, so you +# only have to do the calculation once. +# +# A number of the above factors, however, can be set after we've already +# been asked to return a string for a Node, because a Repository() or +# VariantDir() call or the like may not occur until later in SConscript +# files. So this variable controls whether we bother trying to save +# string values for Nodes. The wrapper interface can set this whenever +# they're done mucking with Repository and VariantDir and the other stuff, +# to let this module know it can start returning saved string values +# for Nodes. +# +Save_Strings = None + +def save_strings(val): + global Save_Strings + Save_Strings = val + +# +# Avoid unnecessary function calls by recording a Boolean value that +# tells us whether or not os.path.splitdrive() actually does anything +# on this system, and therefore whether we need to bother calling it +# when looking up path names in various methods below. +# + +do_splitdrive = None +_my_splitdrive =None + +def initialize_do_splitdrive(): + global do_splitdrive + global has_unc + drive, path = os.path.splitdrive('X:/foo') + has_unc = hasattr(os.path, 'splitunc') + + do_splitdrive = not not drive or has_unc + + global _my_splitdrive + if has_unc: + def splitdrive(p): + if p[1:2] == ':': + return p[:2], p[2:] + if p[0:2] == '//': + # Note that we leave a leading slash in the path + # because UNC paths are always absolute. + return '//', p[1:] + return '', p + else: + def splitdrive(p): + if p[1:2] == ':': + return p[:2], p[2:] + return '', p + _my_splitdrive = splitdrive + + # Keep some commonly used values in global variables to skip to + # module look-up costs. + global OS_SEP + global UNC_PREFIX + global os_sep_is_slash + + OS_SEP = os.sep + UNC_PREFIX = OS_SEP + OS_SEP + os_sep_is_slash = OS_SEP == '/' + +initialize_do_splitdrive() + +# Used to avoid invoking os.path.normpath if not necessary. +needs_normpath_check = re.compile( + r''' + # We need to renormalize the path if it contains any consecutive + # '/' characters. + .*// | + + # We need to renormalize the path if it contains a '..' directory. + # Note that we check for all the following cases: + # + # a) The path is a single '..' + # b) The path starts with '..'. E.g. '../' or '../moredirs' + # but we not match '..abc/'. + # c) The path ends with '..'. E.g. '/..' or 'dirs/..' + # d) The path contains a '..' in the middle. + # E.g. dirs/../moredirs + + (.*/)?\.\.(?:/|$) | + + # We need to renormalize the path if it contains a '.' + # directory, but NOT if it is a single '.' '/' characters. We + # do not want to match a single '.' because this case is checked + # for explicitely since this is common enough case. + # + # Note that we check for all the following cases: + # + # a) We don't match a single '.' + # b) We match if the path starts with '.'. E.g. './' or + # './moredirs' but we not match '.abc/'. + # c) We match if the path ends with '.'. E.g. '/.' or + # 'dirs/.' + # d) We match if the path contains a '.' in the middle. + # E.g. dirs/./moredirs + + \./|.*/\.(?:/|$) + + ''', + re.VERBOSE + ) +needs_normpath_match = needs_normpath_check.match + +# +# SCons.Action objects for interacting with the outside world. +# +# The Node.FS methods in this module should use these actions to +# create and/or remove files and directories; they should *not* use +# os.{link,symlink,unlink,mkdir}(), etc., directly. +# +# Using these SCons.Action objects ensures that descriptions of these +# external activities are properly displayed, that the displays are +# suppressed when the -s (silent) option is used, and (most importantly) +# the actions are disabled when the the -n option is used, in which case +# there should be *no* changes to the external file system(s)... +# + +if hasattr(os, 'link'): + def _hardlink_func(fs, src, dst): + # If the source is a symlink, we can't just hard-link to it + # because a relative symlink may point somewhere completely + # different. We must disambiguate the symlink and then + # hard-link the final destination file. + while fs.islink(src): + link = fs.readlink(src) + if not os.path.isabs(link): + src = link + else: + src = os.path.join(os.path.dirname(src), link) + fs.link(src, dst) +else: + _hardlink_func = None + +if hasattr(os, 'symlink'): + def _softlink_func(fs, src, dst): + fs.symlink(src, dst) +else: + _softlink_func = None + +def _copy_func(fs, src, dest): + shutil.copy2(src, dest) + st = fs.stat(src) + fs.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) + + +Valid_Duplicates = ['hard-soft-copy', 'soft-hard-copy', + 'hard-copy', 'soft-copy', 'copy'] + +Link_Funcs = [] # contains the callables of the specified duplication style + +def set_duplicate(duplicate): + # Fill in the Link_Funcs list according to the argument + # (discarding those not available on the platform). + + # Set up the dictionary that maps the argument names to the + # underlying implementations. We do this inside this function, + # not in the top-level module code, so that we can remap os.link + # and os.symlink for testing purposes. + link_dict = { + 'hard' : _hardlink_func, + 'soft' : _softlink_func, + 'copy' : _copy_func + } + + if not duplicate in Valid_Duplicates: + raise SCons.Errors.InternalError("The argument of set_duplicate " + "should be in Valid_Duplicates") + global Link_Funcs + Link_Funcs = [] + for func in duplicate.split('-'): + if link_dict[func]: + Link_Funcs.append(link_dict[func]) + +def LinkFunc(target, source, env): + # Relative paths cause problems with symbolic links, so + # we use absolute paths, which may be a problem for people + # who want to move their soft-linked src-trees around. Those + # people should use the 'hard-copy' mode, softlinks cannot be + # used for that; at least I have no idea how ... + src = source[0].abspath + dest = target[0].abspath + dir, file = os.path.split(dest) + if dir and not target[0].fs.isdir(dir): + os.makedirs(dir) + if not Link_Funcs: + # Set a default order of link functions. + set_duplicate('hard-soft-copy') + fs = source[0].fs + # Now link the files with the previously specified order. + for func in Link_Funcs: + try: + func(fs, src, dest) + break + except (IOError, OSError): + # An OSError indicates something happened like a permissions + # problem or an attempt to symlink across file-system + # boundaries. An IOError indicates something like the file + # not existing. In either case, keeping trying additional + # functions in the list and only raise an error if the last + # one failed. + if func == Link_Funcs[-1]: + # exception of the last link method (copy) are fatal + raise + return 0 + +Link = SCons.Action.Action(LinkFunc, None) +def LocalString(target, source, env): + return 'Local copy of %s from %s' % (target[0], source[0]) + +LocalCopy = SCons.Action.Action(LinkFunc, LocalString) + +def UnlinkFunc(target, source, env): + t = target[0] + t.fs.unlink(t.abspath) + return 0 + +Unlink = SCons.Action.Action(UnlinkFunc, None) + +def MkdirFunc(target, source, env): + t = target[0] + if not t.exists(): + t.fs.mkdir(t.abspath) + return 0 + +Mkdir = SCons.Action.Action(MkdirFunc, None, presub=None) + +MkdirBuilder = None + +def get_MkdirBuilder(): + global MkdirBuilder + if MkdirBuilder is None: + import SCons.Builder + import SCons.Defaults + # "env" will get filled in by Executor.get_build_env() + # calling SCons.Defaults.DefaultEnvironment() when necessary. + MkdirBuilder = SCons.Builder.Builder(action = Mkdir, + env = None, + explain = None, + is_explicit = None, + target_scanner = SCons.Defaults.DirEntryScanner, + name = "MkdirBuilder") + return MkdirBuilder + +class _Null(object): + pass + +_null = _Null() + +DefaultSCCSBuilder = None +DefaultRCSBuilder = None + +def get_DefaultSCCSBuilder(): + global DefaultSCCSBuilder + if DefaultSCCSBuilder is None: + import SCons.Builder + # "env" will get filled in by Executor.get_build_env() + # calling SCons.Defaults.DefaultEnvironment() when necessary. + act = SCons.Action.Action('$SCCSCOM', '$SCCSCOMSTR') + DefaultSCCSBuilder = SCons.Builder.Builder(action = act, + env = None, + name = "DefaultSCCSBuilder") + return DefaultSCCSBuilder + +def get_DefaultRCSBuilder(): + global DefaultRCSBuilder + if DefaultRCSBuilder is None: + import SCons.Builder + # "env" will get filled in by Executor.get_build_env() + # calling SCons.Defaults.DefaultEnvironment() when necessary. + act = SCons.Action.Action('$RCS_COCOM', '$RCS_COCOMSTR') + DefaultRCSBuilder = SCons.Builder.Builder(action = act, + env = None, + name = "DefaultRCSBuilder") + return DefaultRCSBuilder + +# Cygwin's os.path.normcase pretends it's on a case-sensitive filesystem. +_is_cygwin = sys.platform == "cygwin" +if os.path.normcase("TeSt") == os.path.normpath("TeSt") and not _is_cygwin: + def _my_normcase(x): + return x +else: + def _my_normcase(x): + return x.upper() + + + +class DiskChecker(object): + def __init__(self, type, do, ignore): + self.type = type + self.do = do + self.ignore = ignore + self.func = do + def __call__(self, *args, **kw): + return self.func(*args, **kw) + def set(self, list): + if self.type in list: + self.func = self.do + else: + self.func = self.ignore + +def do_diskcheck_match(node, predicate, errorfmt): + result = predicate() + try: + # If calling the predicate() cached a None value from stat(), + # remove it so it doesn't interfere with later attempts to + # build this Node as we walk the DAG. (This isn't a great way + # to do this, we're reaching into an interface that doesn't + # really belong to us, but it's all about performance, so + # for now we'll just document the dependency...) + if node._memo['stat'] is None: + del node._memo['stat'] + except (AttributeError, KeyError): + pass + if result: + raise TypeError(errorfmt % node.abspath) + +def ignore_diskcheck_match(node, predicate, errorfmt): + pass + +def do_diskcheck_rcs(node, name): + try: + rcs_dir = node.rcs_dir + except AttributeError: + if node.entry_exists_on_disk('RCS'): + rcs_dir = node.Dir('RCS') + else: + rcs_dir = None + node.rcs_dir = rcs_dir + if rcs_dir: + return rcs_dir.entry_exists_on_disk(name+',v') + return None + +def ignore_diskcheck_rcs(node, name): + return None + +def do_diskcheck_sccs(node, name): + try: + sccs_dir = node.sccs_dir + except AttributeError: + if node.entry_exists_on_disk('SCCS'): + sccs_dir = node.Dir('SCCS') + else: + sccs_dir = None + node.sccs_dir = sccs_dir + if sccs_dir: + return sccs_dir.entry_exists_on_disk('s.'+name) + return None + +def ignore_diskcheck_sccs(node, name): + return None + +diskcheck_match = DiskChecker('match', do_diskcheck_match, ignore_diskcheck_match) +diskcheck_rcs = DiskChecker('rcs', do_diskcheck_rcs, ignore_diskcheck_rcs) +diskcheck_sccs = DiskChecker('sccs', do_diskcheck_sccs, ignore_diskcheck_sccs) + +diskcheckers = [ + diskcheck_match, + diskcheck_rcs, + diskcheck_sccs, +] + +def set_diskcheck(list): + for dc in diskcheckers: + dc.set(list) + +def diskcheck_types(): + return [dc.type for dc in diskcheckers] + + + +class EntryProxy(SCons.Util.Proxy): + + __str__ = SCons.Util.Delegate('__str__') + + def __get_abspath(self): + entry = self.get() + return SCons.Subst.SpecialAttrWrapper(entry.get_abspath(), + entry.name + "_abspath") + + def __get_filebase(self): + name = self.get().name + return SCons.Subst.SpecialAttrWrapper(SCons.Util.splitext(name)[0], + name + "_filebase") + + def __get_suffix(self): + name = self.get().name + return SCons.Subst.SpecialAttrWrapper(SCons.Util.splitext(name)[1], + name + "_suffix") + + def __get_file(self): + name = self.get().name + return SCons.Subst.SpecialAttrWrapper(name, name + "_file") + + def __get_base_path(self): + """Return the file's directory and file name, with the + suffix stripped.""" + entry = self.get() + return SCons.Subst.SpecialAttrWrapper(SCons.Util.splitext(entry.get_path())[0], + entry.name + "_base") + + def __get_posix_path(self): + """Return the path with / as the path separator, + regardless of platform.""" + if os_sep_is_slash: + return self + else: + entry = self.get() + r = entry.get_path().replace(OS_SEP, '/') + return SCons.Subst.SpecialAttrWrapper(r, entry.name + "_posix") + + def __get_windows_path(self): + """Return the path with \ as the path separator, + regardless of platform.""" + if OS_SEP == '\\': + return self + else: + entry = self.get() + r = entry.get_path().replace(OS_SEP, '\\') + return SCons.Subst.SpecialAttrWrapper(r, entry.name + "_windows") + + def __get_srcnode(self): + return EntryProxy(self.get().srcnode()) + + def __get_srcdir(self): + """Returns the directory containing the source node linked to this + node via VariantDir(), or the directory of this node if not linked.""" + return EntryProxy(self.get().srcnode().dir) + + def __get_rsrcnode(self): + return EntryProxy(self.get().srcnode().rfile()) + + def __get_rsrcdir(self): + """Returns the directory containing the source node linked to this + node via VariantDir(), or the directory of this node if not linked.""" + return EntryProxy(self.get().srcnode().rfile().dir) + + def __get_dir(self): + return EntryProxy(self.get().dir) + + dictSpecialAttrs = { "base" : __get_base_path, + "posix" : __get_posix_path, + "windows" : __get_windows_path, + "win32" : __get_windows_path, + "srcpath" : __get_srcnode, + "srcdir" : __get_srcdir, + "dir" : __get_dir, + "abspath" : __get_abspath, + "filebase" : __get_filebase, + "suffix" : __get_suffix, + "file" : __get_file, + "rsrcpath" : __get_rsrcnode, + "rsrcdir" : __get_rsrcdir, + } + + def __getattr__(self, name): + # This is how we implement the "special" attributes + # such as base, posix, srcdir, etc. + try: + attr_function = self.dictSpecialAttrs[name] + except KeyError: + try: + attr = SCons.Util.Proxy.__getattr__(self, name) + except AttributeError, e: + # Raise our own AttributeError subclass with an + # overridden __str__() method that identifies the + # name of the entry that caused the exception. + raise EntryProxyAttributeError(self, name) + return attr + else: + return attr_function(self) + +class Base(SCons.Node.Node): + """A generic class for file system entries. This class is for + when we don't know yet whether the entry being looked up is a file + or a directory. Instances of this class can morph into either + Dir or File objects by a later, more precise lookup. + + Note: this class does not define __cmp__ and __hash__ for + efficiency reasons. SCons does a lot of comparing of + Node.FS.{Base,Entry,File,Dir} objects, so those operations must be + as fast as possible, which means we want to use Python's built-in + object identity comparisons. + """ + + memoizer_counters = [] + + def __init__(self, name, directory, fs): + """Initialize a generic Node.FS.Base object. + + Call the superclass initialization, take care of setting up + our relative and absolute paths, identify our parent + directory, and indicate that this node should use + signatures.""" + if __debug__: logInstanceCreation(self, 'Node.FS.Base') + SCons.Node.Node.__init__(self) + + # Filenames and paths are probably reused and are intern'ed to + # save some memory. + + #: Filename with extension as it was specified when the object was + #: created; to obtain filesystem path, use Python str() function + self.name = SCons.Util.silent_intern(name) + #: Cached filename extension + self.suffix = SCons.Util.silent_intern(SCons.Util.splitext(name)[1]) + self.fs = fs #: Reference to parent Node.FS object + + assert directory, "A directory must be provided" + + self.abspath = SCons.Util.silent_intern(directory.entry_abspath(name)) + self.labspath = SCons.Util.silent_intern(directory.entry_labspath(name)) + if directory.path == '.': + self.path = SCons.Util.silent_intern(name) + else: + self.path = SCons.Util.silent_intern(directory.entry_path(name)) + if directory.tpath == '.': + self.tpath = SCons.Util.silent_intern(name) + else: + self.tpath = SCons.Util.silent_intern(directory.entry_tpath(name)) + self.path_elements = directory.path_elements + [self] + + self.dir = directory + self.cwd = None # will hold the SConscript directory for target nodes + self.duplicate = directory.duplicate + + def str_for_display(self): + return '"' + self.__str__() + '"' + + def must_be_same(self, klass): + """ + This node, which already existed, is being looked up as the + specified klass. Raise an exception if it isn't. + """ + if isinstance(self, klass) or klass is Entry: + return + raise TypeError("Tried to lookup %s '%s' as a %s." %\ + (self.__class__.__name__, self.path, klass.__name__)) + + def get_dir(self): + return self.dir + + def get_suffix(self): + return self.suffix + + def rfile(self): + return self + + def __str__(self): + """A Node.FS.Base object's string representation is its path + name.""" + global Save_Strings + if Save_Strings: + return self._save_str() + return self._get_str() + + memoizer_counters.append(SCons.Memoize.CountValue('_save_str')) + + def _save_str(self): + try: + return self._memo['_save_str'] + except KeyError: + pass + result = sys.intern(self._get_str()) + self._memo['_save_str'] = result + return result + + def _get_str(self): + global Save_Strings + if self.duplicate or self.is_derived(): + return self.get_path() + srcnode = self.srcnode() + if srcnode.stat() is None and self.stat() is not None: + result = self.get_path() + else: + result = srcnode.get_path() + if not Save_Strings: + # We're not at the point where we're saving the string + # representations of FS Nodes (because we haven't finished + # reading the SConscript files and need to have str() return + # things relative to them). That also means we can't yet + # cache values returned (or not returned) by stat(), since + # Python code in the SConscript files might still create + # or otherwise affect the on-disk file. So get rid of the + # values that the underlying stat() method saved. + try: del self._memo['stat'] + except KeyError: pass + if self is not srcnode: + try: del srcnode._memo['stat'] + except KeyError: pass + return result + + rstr = __str__ + + memoizer_counters.append(SCons.Memoize.CountValue('stat')) + + def stat(self): + try: return self._memo['stat'] + except KeyError: pass + try: result = self.fs.stat(self.abspath) + except os.error: result = None + self._memo['stat'] = result + return result + + def exists(self): + return self.stat() is not None + + def rexists(self): + return self.rfile().exists() + + def getmtime(self): + st = self.stat() + if st: return st[stat.ST_MTIME] + else: return None + + def getsize(self): + st = self.stat() + if st: return st[stat.ST_SIZE] + else: return None + + def isdir(self): + st = self.stat() + return st is not None and stat.S_ISDIR(st[stat.ST_MODE]) + + def isfile(self): + st = self.stat() + return st is not None and stat.S_ISREG(st[stat.ST_MODE]) + + if hasattr(os, 'symlink'): + def islink(self): + try: st = self.fs.lstat(self.abspath) + except os.error: return 0 + return stat.S_ISLNK(st[stat.ST_MODE]) + else: + def islink(self): + return 0 # no symlinks + + def is_under(self, dir): + if self is dir: + return 1 + else: + return self.dir.is_under(dir) + + def set_local(self): + self._local = 1 + + def srcnode(self): + """If this node is in a build path, return the node + corresponding to its source file. Otherwise, return + ourself. + """ + srcdir_list = self.dir.srcdir_list() + if srcdir_list: + srcnode = srcdir_list[0].Entry(self.name) + srcnode.must_be_same(self.__class__) + return srcnode + return self + + def get_path(self, dir=None): + """Return path relative to the current working directory of the + Node.FS.Base object that owns us.""" + if not dir: + dir = self.fs.getcwd() + if self == dir: + return '.' + path_elems = self.path_elements + pathname = '' + try: i = path_elems.index(dir) + except ValueError: + for p in path_elems[:-1]: + pathname += p.dirname + else: + for p in path_elems[i+1:-1]: + pathname += p.dirname + return pathname + path_elems[-1].name + + def set_src_builder(self, builder): + """Set the source code builder for this node.""" + self.sbuilder = builder + if not self.has_builder(): + self.builder_set(builder) + + def src_builder(self): + """Fetch the source code builder for this node. + + If there isn't one, we cache the source code builder specified + for the directory (which in turn will cache the value from its + parent directory, and so on up to the file system root). + """ + try: + scb = self.sbuilder + except AttributeError: + scb = self.dir.src_builder() + self.sbuilder = scb + return scb + + def get_abspath(self): + """Get the absolute path of the file.""" + return self.abspath + + def for_signature(self): + # Return just our name. Even an absolute path would not work, + # because that can change thanks to symlinks or remapped network + # paths. + return self.name + + def get_subst_proxy(self): + try: + return self._proxy + except AttributeError: + ret = EntryProxy(self) + self._proxy = ret + return ret + + def target_from_source(self, prefix, suffix, splitext=SCons.Util.splitext): + """ + + Generates a target entry that corresponds to this entry (usually + a source file) with the specified prefix and suffix. + + Note that this method can be overridden dynamically for generated + files that need different behavior. See Tool/swig.py for + an example. + """ + return self.dir.Entry(prefix + splitext(self.name)[0] + suffix) + + def _Rfindalldirs_key(self, pathlist): + return pathlist + + memoizer_counters.append(SCons.Memoize.CountDict('Rfindalldirs', _Rfindalldirs_key)) + + def Rfindalldirs(self, pathlist): + """ + Return all of the directories for a given path list, including + corresponding "backing" directories in any repositories. + + The Node lookups are relative to this Node (typically a + directory), so memoizing result saves cycles from looking + up the same path for each target in a given directory. + """ + try: + memo_dict = self._memo['Rfindalldirs'] + except KeyError: + memo_dict = {} + self._memo['Rfindalldirs'] = memo_dict + else: + try: + return memo_dict[pathlist] + except KeyError: + pass + + create_dir_relative_to_self = self.Dir + result = [] + for path in pathlist: + if isinstance(path, SCons.Node.Node): + result.append(path) + else: + dir = create_dir_relative_to_self(path) + result.extend(dir.get_all_rdirs()) + + memo_dict[pathlist] = result + + return result + + def RDirs(self, pathlist): + """Search for a list of directories in the Repository list.""" + cwd = self.cwd or self.fs._cwd + return cwd.Rfindalldirs(pathlist) + + memoizer_counters.append(SCons.Memoize.CountValue('rentry')) + + def rentry(self): + try: + return self._memo['rentry'] + except KeyError: + pass + result = self + if not self.exists(): + norm_name = _my_normcase(self.name) + for dir in self.dir.get_all_rdirs(): + try: + node = dir.entries[norm_name] + except KeyError: + if dir.entry_exists_on_disk(self.name): + result = dir.Entry(self.name) + break + self._memo['rentry'] = result + return result + + def _glob1(self, pattern, ondisk=True, source=False, strings=False): + return [] + +class Entry(Base): + """This is the class for generic Node.FS entries--that is, things + that could be a File or a Dir, but we're just not sure yet. + Consequently, the methods in this class really exist just to + transform their associated object into the right class when the + time comes, and then call the same-named method in the transformed + class.""" + + def diskcheck_match(self): + pass + + def disambiguate(self, must_exist=None): + """ + """ + if self.isdir(): + self.__class__ = Dir + self._morph() + elif self.isfile(): + self.__class__ = File + self._morph() + self.clear() + else: + # There was nothing on-disk at this location, so look in + # the src directory. + # + # We can't just use self.srcnode() straight away because + # that would create an actual Node for this file in the src + # directory, and there might not be one. Instead, use the + # dir_on_disk() method to see if there's something on-disk + # with that name, in which case we can go ahead and call + # self.srcnode() to create the right type of entry. + srcdir = self.dir.srcnode() + if srcdir != self.dir and \ + srcdir.entry_exists_on_disk(self.name) and \ + self.srcnode().isdir(): + self.__class__ = Dir + self._morph() + elif must_exist: + msg = "No such file or directory: '%s'" % self.abspath + raise SCons.Errors.UserError(msg) + else: + self.__class__ = File + self._morph() + self.clear() + return self + + def rfile(self): + """We're a generic Entry, but the caller is actually looking for + a File at this point, so morph into one.""" + self.__class__ = File + self._morph() + self.clear() + return File.rfile(self) + + def scanner_key(self): + return self.get_suffix() + + def get_contents(self): + """Fetch the contents of the entry. Returns the exact binary + contents of the file.""" + try: + self = self.disambiguate(must_exist=1) + except SCons.Errors.UserError: + # There was nothing on disk with which to disambiguate + # this entry. Leave it as an Entry, but return a null + # string so calls to get_contents() in emitters and the + # like (e.g. in qt.py) don't have to disambiguate by hand + # or catch the exception. + return '' + else: + return self.get_contents() + + def get_text_contents(self): + """Fetch the decoded text contents of a Unicode encoded Entry. + + Since this should return the text contents from the file + system, we check to see into what sort of subclass we should + morph this Entry.""" + try: + self = self.disambiguate(must_exist=1) + except SCons.Errors.UserError: + # There was nothing on disk with which to disambiguate + # this entry. Leave it as an Entry, but return a null + # string so calls to get_text_contents() in emitters and + # the like (e.g. in qt.py) don't have to disambiguate by + # hand or catch the exception. + return '' + else: + return self.get_text_contents() + + def must_be_same(self, klass): + """Called to make sure a Node is a Dir. Since we're an + Entry, we can morph into one.""" + if self.__class__ is not klass: + self.__class__ = klass + self._morph() + self.clear() + + # The following methods can get called before the Taskmaster has + # had a chance to call disambiguate() directly to see if this Entry + # should really be a Dir or a File. We therefore use these to call + # disambiguate() transparently (from our caller's point of view). + # + # Right now, this minimal set of methods has been derived by just + # looking at some of the methods that will obviously be called early + # in any of the various Taskmasters' calling sequences, and then + # empirically figuring out which additional methods are necessary + # to make various tests pass. + + def exists(self): + """Return if the Entry exists. Check the file system to see + what we should turn into first. Assume a file if there's no + directory.""" + return self.disambiguate().exists() + + def rel_path(self, other): + d = self.disambiguate() + if d.__class__ is Entry: + raise Exception("rel_path() could not disambiguate File/Dir") + return d.rel_path(other) + + def new_ninfo(self): + return self.disambiguate().new_ninfo() + + def changed_since_last_build(self, target, prev_ni): + return self.disambiguate().changed_since_last_build(target, prev_ni) + + def _glob1(self, pattern, ondisk=True, source=False, strings=False): + return self.disambiguate()._glob1(pattern, ondisk, source, strings) + + def get_subst_proxy(self): + return self.disambiguate().get_subst_proxy() + +# This is for later so we can differentiate between Entry the class and Entry +# the method of the FS class. +_classEntry = Entry + + +class LocalFS(object): + + if SCons.Memoize.use_memoizer: + __metaclass__ = SCons.Memoize.Memoized_Metaclass + + # This class implements an abstraction layer for operations involving + # a local file system. Essentially, this wraps any function in + # the os, os.path or shutil modules that we use to actually go do + # anything with or to the local file system. + # + # Note that there's a very good chance we'll refactor this part of + # the architecture in some way as we really implement the interface(s) + # for remote file system Nodes. For example, the right architecture + # might be to have this be a subclass instead of a base class. + # Nevertheless, we're using this as a first step in that direction. + # + # We're not using chdir() yet because the calling subclass method + # needs to use os.chdir() directly to avoid recursion. Will we + # really need this one? + #def chdir(self, path): + # return os.chdir(path) + def chmod(self, path, mode): + return os.chmod(path, mode) + def copy(self, src, dst): + return shutil.copy(src, dst) + def copy2(self, src, dst): + return shutil.copy2(src, dst) + def exists(self, path): + return os.path.exists(path) + def getmtime(self, path): + return os.path.getmtime(path) + def getsize(self, path): + return os.path.getsize(path) + def isdir(self, path): + return os.path.isdir(path) + def isfile(self, path): + return os.path.isfile(path) + def link(self, src, dst): + return os.link(src, dst) + def lstat(self, path): + return os.lstat(path) + def listdir(self, path): + return os.listdir(path) + def makedirs(self, path): + return os.makedirs(path) + def mkdir(self, path): + return os.mkdir(path) + def rename(self, old, new): + return os.rename(old, new) + def stat(self, path): + return os.stat(path) + def symlink(self, src, dst): + return os.symlink(src, dst) + def open(self, path): + return open(path) + def unlink(self, path): + return os.unlink(path) + + if hasattr(os, 'symlink'): + def islink(self, path): + return os.path.islink(path) + else: + def islink(self, path): + return 0 # no symlinks + + if hasattr(os, 'readlink'): + def readlink(self, file): + return os.readlink(file) + else: + def readlink(self, file): + return '' + + +#class RemoteFS: +# # Skeleton for the obvious methods we might need from the +# # abstraction layer for a remote filesystem. +# def upload(self, local_src, remote_dst): +# pass +# def download(self, remote_src, local_dst): +# pass + + +class FS(LocalFS): + + memoizer_counters = [] + + def __init__(self, path = None): + """Initialize the Node.FS subsystem. + + The supplied path is the top of the source tree, where we + expect to find the top-level build file. If no path is + supplied, the current directory is the default. + + The path argument must be a valid absolute path. + """ + if __debug__: logInstanceCreation(self, 'Node.FS') + + self._memo = {} + + self.Root = {} + self.SConstruct_dir = None + self.max_drift = default_max_drift + + self.Top = None + if path is None: + self.pathTop = os.getcwd() + else: + self.pathTop = path + self.defaultDrive = _my_normcase(_my_splitdrive(self.pathTop)[0]) + + self.Top = self.Dir(self.pathTop) + self.Top.path = '.' + self.Top.tpath = '.' + self._cwd = self.Top + + DirNodeInfo.fs = self + FileNodeInfo.fs = self + + def set_SConstruct_dir(self, dir): + self.SConstruct_dir = dir + + def get_max_drift(self): + return self.max_drift + + def set_max_drift(self, max_drift): + self.max_drift = max_drift + + def getcwd(self): + if hasattr(self, "_cwd"): + return self._cwd + else: + return "" + + def chdir(self, dir, change_os_dir=0): + """Change the current working directory for lookups. + If change_os_dir is true, we will also change the "real" cwd + to match. + """ + curr=self._cwd + try: + if dir is not None: + self._cwd = dir + if change_os_dir: + os.chdir(dir.abspath) + except OSError: + self._cwd = curr + raise + + def get_root(self, drive): + """ + Returns the root directory for the specified drive, creating + it if necessary. + """ + drive = _my_normcase(drive) + try: + return self.Root[drive] + except KeyError: + root = RootDir(drive, self) + self.Root[drive] = root + if not drive: + self.Root[self.defaultDrive] = root + elif drive == self.defaultDrive: + self.Root[''] = root + return root + + def _lookup(self, p, directory, fsclass, create=1): + """ + The generic entry point for Node lookup with user-supplied data. + + This translates arbitrary input into a canonical Node.FS object + of the specified fsclass. The general approach for strings is + to turn it into a fully normalized absolute path and then call + the root directory's lookup_abs() method for the heavy lifting. + + If the path name begins with '#', it is unconditionally + interpreted relative to the top-level directory of this FS. '#' + is treated as a synonym for the top-level SConstruct directory, + much like '~' is treated as a synonym for the user's home + directory in a UNIX shell. So both '#foo' and '#/foo' refer + to the 'foo' subdirectory underneath the top-level SConstruct + directory. + + If the path name is relative, then the path is looked up relative + to the specified directory, or the current directory (self._cwd, + typically the SConscript directory) if the specified directory + is None. + """ + if isinstance(p, Base): + # It's already a Node.FS object. Make sure it's the right + # class and return. + p.must_be_same(fsclass) + return p + # str(p) in case it's something like a proxy object + p = str(p) + + if not os_sep_is_slash: + p = p.replace(OS_SEP, '/') + + if p[0:1] == '#': + # There was an initial '#', so we strip it and override + # whatever directory they may have specified with the + # top-level SConstruct directory. + p = p[1:] + directory = self.Top + + # There might be a drive letter following the + # '#'. Although it is not described in the SCons man page, + # the regression test suite explicitly tests for that + # syntax. It seems to mean the following thing: + # + # Assuming the the SCons top dir is in C:/xxx/yyy, + # '#X:/toto' means X:/xxx/yyy/toto. + # + # i.e. it assumes that the X: drive has a directory + # structure similar to the one found on drive C:. + if do_splitdrive: + drive, p = _my_splitdrive(p) + if drive: + root = self.get_root(drive) + else: + root = directory.root + else: + root = directory.root + + # We can only strip trailing after splitting the drive + # since the drive might the UNC '//' prefix. + p = p.strip('/') + + needs_normpath = needs_normpath_match(p) + + # The path is relative to the top-level SCons directory. + if p in ('', '.'): + p = directory.labspath + else: + p = directory.labspath + '/' + p + else: + if do_splitdrive: + drive, p = _my_splitdrive(p) + if drive and not p: + # This causes a naked drive letter to be treated + # as a synonym for the root directory on that + # drive. + p = '/' + else: + drive = '' + + # We can only strip trailing '/' since the drive might the + # UNC '//' prefix. + if p != '/': + p = p.rstrip('/') + + needs_normpath = needs_normpath_match(p) + + if p[0:1] == '/': + # Absolute path + root = self.get_root(drive) + else: + # This is a relative lookup or to the current directory + # (the path name is not absolute). Add the string to the + # appropriate directory lookup path, after which the whole + # thing gets normalized. + if directory: + if not isinstance(directory, Dir): + directory = self.Dir(directory) + else: + directory = self._cwd + + if p in ('', '.'): + p = directory.labspath + else: + p = directory.labspath + '/' + p + + if drive: + root = self.get_root(drive) + else: + root = directory.root + + if needs_normpath is not None: + # Normalize a pathname. Will return the same result for + # equivalent paths. + # + # We take advantage of the fact that we have an absolute + # path here for sure. In addition, we know that the + # components of lookup path are separated by slashes at + # this point. Because of this, this code is about 2X + # faster than calling os.path.normpath() followed by + # replacing os.sep with '/' again. + ins = p.split('/')[1:] + outs = [] + for d in ins: + if d == '..': + try: + outs.pop() + except IndexError: + pass + elif d not in ('', '.'): + outs.append(d) + p = '/' + '/'.join(outs) + + return root._lookup_abs(p, fsclass, create) + + def Entry(self, name, directory = None, create = 1): + """Look up or create a generic Entry node with the specified name. + If the name is a relative path (begins with ./, ../, or a file + name), then it is looked up relative to the supplied directory + node, or to the top level directory of the FS (supplied at + construction time) if no directory is supplied. + """ + return self._lookup(name, directory, Entry, create) + + def File(self, name, directory = None, create = 1): + """Look up or create a File node with the specified name. If + the name is a relative path (begins with ./, ../, or a file name), + then it is looked up relative to the supplied directory node, + or to the top level directory of the FS (supplied at construction + time) if no directory is supplied. + + This method will raise TypeError if a directory is found at the + specified path. + """ + return self._lookup(name, directory, File, create) + + def Dir(self, name, directory = None, create = True): + """Look up or create a Dir node with the specified name. If + the name is a relative path (begins with ./, ../, or a file name), + then it is looked up relative to the supplied directory node, + or to the top level directory of the FS (supplied at construction + time) if no directory is supplied. + + This method will raise TypeError if a normal file is found at the + specified path. + """ + return self._lookup(name, directory, Dir, create) + + def VariantDir(self, variant_dir, src_dir, duplicate=1): + """Link the supplied variant directory to the source directory + for purposes of building files.""" + + if not isinstance(src_dir, SCons.Node.Node): + src_dir = self.Dir(src_dir) + if not isinstance(variant_dir, SCons.Node.Node): + variant_dir = self.Dir(variant_dir) + if src_dir.is_under(variant_dir): + raise SCons.Errors.UserError("Source directory cannot be under variant directory.") + if variant_dir.srcdir: + if variant_dir.srcdir == src_dir: + return # We already did this. + raise SCons.Errors.UserError("'%s' already has a source directory: '%s'."%(variant_dir, variant_dir.srcdir)) + variant_dir.link(src_dir, duplicate) + + def Repository(self, *dirs): + """Specify Repository directories to search.""" + for d in dirs: + if not isinstance(d, SCons.Node.Node): + d = self.Dir(d) + self.Top.addRepository(d) + + def variant_dir_target_climb(self, orig, dir, tail): + """Create targets in corresponding variant directories + + Climb the directory tree, and look up path names + relative to any linked variant directories we find. + + Even though this loops and walks up the tree, we don't memoize + the return value because this is really only used to process + the command-line targets. + """ + targets = [] + message = None + fmt = "building associated VariantDir targets: %s" + start_dir = dir + while dir: + for bd in dir.variant_dirs: + if start_dir.is_under(bd): + # If already in the build-dir location, don't reflect + return [orig], fmt % str(orig) + p = os.path.join(bd.path, *tail) + targets.append(self.Entry(p)) + tail = [dir.name] + tail + dir = dir.up() + if targets: + message = fmt % ' '.join(map(str, targets)) + return targets, message + + def Glob(self, pathname, ondisk=True, source=True, strings=False, cwd=None): + """ + Globs + + This is mainly a shim layer + """ + if cwd is None: + cwd = self.getcwd() + return cwd.glob(pathname, ondisk, source, strings) + +class DirNodeInfo(SCons.Node.NodeInfoBase): + # This should get reset by the FS initialization. + current_version_id = 1 + + fs = None + + def str_to_node(self, s): + top = self.fs.Top + root = top.root + if do_splitdrive: + drive, s = _my_splitdrive(s) + if drive: + root = self.fs.get_root(drive) + if not os.path.isabs(s): + s = top.labspath + '/' + s + return root._lookup_abs(s, Entry) + +class DirBuildInfo(SCons.Node.BuildInfoBase): + current_version_id = 1 + +glob_magic_check = re.compile('[*?[]') + +def has_glob_magic(s): + return glob_magic_check.search(s) is not None + +class Dir(Base): + """A class for directories in a file system. + """ + + memoizer_counters = [] + + NodeInfo = DirNodeInfo + BuildInfo = DirBuildInfo + + def __init__(self, name, directory, fs): + if __debug__: logInstanceCreation(self, 'Node.FS.Dir') + Base.__init__(self, name, directory, fs) + self._morph() + + def _morph(self): + """Turn a file system Node (either a freshly initialized directory + object or a separate Entry object) into a proper directory object. + + Set up this directory's entries and hook it into the file + system tree. Specify that directories (this Node) don't use + signatures for calculating whether they're current. + """ + + self.repositories = [] + self.srcdir = None + + self.entries = {} + self.entries['.'] = self + self.entries['..'] = self.dir + self.cwd = self + self.searched = 0 + self._sconsign = None + self.variant_dirs = [] + self.root = self.dir.root + + # For directories, we make a difference between the directory + # 'name' and the directory 'dirname'. The 'name' attribute is + # used when we need to print the 'name' of the directory or + # when we it is used as the last part of a path. The 'dirname' + # is used when the directory is not the last element of the + # path. The main reason for making that distinction is that + # for RoorDir's the dirname can not be easily inferred from + # the name. For example, we have to add a '/' after a drive + # letter but not after a UNC path prefix ('//'). + self.dirname = self.name + OS_SEP + + # Don't just reset the executor, replace its action list, + # because it might have some pre-or post-actions that need to + # be preserved. + # + # But don't reset the executor if there is a non-null executor + # attached already. The existing executor might have other + # targets, in which case replacing the action list with a + # Mkdir action is a big mistake. + if not hasattr(self, 'executor'): + self.builder = get_MkdirBuilder() + self.get_executor().set_action_list(self.builder.action) + else: + # Prepend MkdirBuilder action to existing action list + l = self.get_executor().action_list + a = get_MkdirBuilder().action + l.insert(0, a) + self.get_executor().set_action_list(l) + + def diskcheck_match(self): + diskcheck_match(self, self.isfile, + "File %s found where directory expected.") + + def __clearRepositoryCache(self, duplicate=None): + """Called when we change the repository(ies) for a directory. + This clears any cached information that is invalidated by changing + the repository.""" + + for node in self.entries.values(): + if node != self.dir: + if node != self and isinstance(node, Dir): + node.__clearRepositoryCache(duplicate) + else: + node.clear() + try: + del node._srcreps + except AttributeError: + pass + if duplicate is not None: + node.duplicate=duplicate + + def __resetDuplicate(self, node): + if node != self: + node.duplicate = node.get_dir().duplicate + + def Entry(self, name): + """ + Looks up or creates an entry node named 'name' relative to + this directory. + """ + return self.fs.Entry(name, self) + + def Dir(self, name, create=True): + """ + Looks up or creates a directory node named 'name' relative to + this directory. + """ + return self.fs.Dir(name, self, create) + + def File(self, name): + """ + Looks up or creates a file node named 'name' relative to + this directory. + """ + return self.fs.File(name, self) + + def link(self, srcdir, duplicate): + """Set this directory as the variant directory for the + supplied source directory.""" + self.srcdir = srcdir + self.duplicate = duplicate + self.__clearRepositoryCache(duplicate) + srcdir.variant_dirs.append(self) + + def getRepositories(self): + """Returns a list of repositories for this directory. + """ + if self.srcdir and not self.duplicate: + return self.srcdir.get_all_rdirs() + self.repositories + return self.repositories + + memoizer_counters.append(SCons.Memoize.CountValue('get_all_rdirs')) + + def get_all_rdirs(self): + try: + return list(self._memo['get_all_rdirs']) + except KeyError: + pass + + result = [self] + fname = '.' + dir = self + while dir: + for rep in dir.getRepositories(): + result.append(rep.Dir(fname)) + if fname == '.': + fname = dir.name + else: + fname = dir.name + OS_SEP + fname + dir = dir.up() + + self._memo['get_all_rdirs'] = list(result) + + return result + + def addRepository(self, dir): + if dir != self and not dir in self.repositories: + self.repositories.append(dir) + dir.tpath = '.' + self.__clearRepositoryCache() + + def up(self): + return self.dir + + def _rel_path_key(self, other): + return str(other) + + memoizer_counters.append(SCons.Memoize.CountDict('rel_path', _rel_path_key)) + + def rel_path(self, other): + """Return a path to "other" relative to this directory. + """ + + # This complicated and expensive method, which constructs relative + # paths between arbitrary Node.FS objects, is no longer used + # by SCons itself. It was introduced to store dependency paths + # in .sconsign files relative to the target, but that ended up + # being significantly inefficient. + # + # We're continuing to support the method because some SConstruct + # files out there started using it when it was available, and + # we're all about backwards compatibility.. + + try: + memo_dict = self._memo['rel_path'] + except KeyError: + memo_dict = {} + self._memo['rel_path'] = memo_dict + else: + try: + return memo_dict[other] + except KeyError: + pass + + if self is other: + result = '.' + + elif not other in self.path_elements: + try: + other_dir = other.get_dir() + except AttributeError: + result = str(other) + else: + if other_dir is None: + result = other.name + else: + dir_rel_path = self.rel_path(other_dir) + if dir_rel_path == '.': + result = other.name + else: + result = dir_rel_path + OS_SEP + other.name + else: + i = self.path_elements.index(other) + 1 + + path_elems = ['..'] * (len(self.path_elements) - i) \ + + [n.name for n in other.path_elements[i:]] + + result = OS_SEP.join(path_elems) + + memo_dict[other] = result + + return result + + def get_env_scanner(self, env, kw={}): + import SCons.Defaults + return SCons.Defaults.DirEntryScanner + + def get_target_scanner(self): + import SCons.Defaults + return SCons.Defaults.DirEntryScanner + + def get_found_includes(self, env, scanner, path): + """Return this directory's implicit dependencies. + + We don't bother caching the results because the scan typically + shouldn't be requested more than once (as opposed to scanning + .h file contents, which can be requested as many times as the + files is #included by other files). + """ + if not scanner: + return [] + # Clear cached info for this Dir. If we already visited this + # directory on our walk down the tree (because we didn't know at + # that point it was being used as the source for another Node) + # then we may have calculated build signature before realizing + # we had to scan the disk. Now that we have to, though, we need + # to invalidate the old calculated signature so that any node + # dependent on our directory structure gets one that includes + # info about everything on disk. + self.clear() + return scanner(self, env, path) + + # + # Taskmaster interface subsystem + # + + def prepare(self): + pass + + def build(self, **kw): + """A null "builder" for directories.""" + global MkdirBuilder + if self.builder is not MkdirBuilder: + SCons.Node.Node.build(self, **kw) + + # + # + # + + def _create(self): + """Create this directory, silently and without worrying about + whether the builder is the default or not.""" + listDirs = [] + parent = self + while parent: + if parent.exists(): + break + listDirs.append(parent) + p = parent.up() + if p is None: + # Don't use while: - else: for this condition because + # if so, then parent is None and has no .path attribute. + raise SCons.Errors.StopError(parent.path) + parent = p + listDirs.reverse() + for dirnode in listDirs: + try: + # Don't call dirnode.build(), call the base Node method + # directly because we definitely *must* create this + # directory. The dirnode.build() method will suppress + # the build if it's the default builder. + SCons.Node.Node.build(dirnode) + dirnode.get_executor().nullify() + # The build() action may or may not have actually + # created the directory, depending on whether the -n + # option was used or not. Delete the _exists and + # _rexists attributes so they can be reevaluated. + dirnode.clear() + except OSError: + pass + + def multiple_side_effect_has_builder(self): + global MkdirBuilder + return self.builder is not MkdirBuilder and self.has_builder() + + def alter_targets(self): + """Return any corresponding targets in a variant directory. + """ + return self.fs.variant_dir_target_climb(self, self, []) + + def scanner_key(self): + """A directory does not get scanned.""" + return None + + def get_text_contents(self): + """We already emit things in text, so just return the binary + version.""" + return self.get_contents() + + def get_contents(self): + """Return content signatures and names of all our children + separated by new-lines. Ensure that the nodes are sorted.""" + contents = [] + for node in sorted(self.children(), key=lambda t: t.name): + contents.append('%s %s\n' % (node.get_csig(), node.name)) + return ''.join(contents) + + def get_csig(self): + """Compute the content signature for Directory nodes. In + general, this is not needed and the content signature is not + stored in the DirNodeInfo. However, if get_contents on a Dir + node is called which has a child directory, the child + directory should return the hash of its contents.""" + contents = self.get_contents() + return SCons.Util.MD5signature(contents) + + def do_duplicate(self, src): + pass + + changed_since_last_build = SCons.Node.Node.state_has_changed + + def is_up_to_date(self): + """If any child is not up-to-date, then this directory isn't, + either.""" + if self.builder is not MkdirBuilder and not self.exists(): + return 0 + up_to_date = SCons.Node.up_to_date + for kid in self.children(): + if kid.get_state() > up_to_date: + return 0 + return 1 + + def rdir(self): + if not self.exists(): + norm_name = _my_normcase(self.name) + for dir in self.dir.get_all_rdirs(): + try: node = dir.entries[norm_name] + except KeyError: node = dir.dir_on_disk(self.name) + if node and node.exists() and \ + (isinstance(dir, Dir) or isinstance(dir, Entry)): + return node + return self + + def sconsign(self): + """Return the .sconsign file info for this directory, + creating it first if necessary.""" + if not self._sconsign: + import SCons.SConsign + self._sconsign = SCons.SConsign.ForDirectory(self) + return self._sconsign + + def srcnode(self): + """Dir has a special need for srcnode()...if we + have a srcdir attribute set, then that *is* our srcnode.""" + if self.srcdir: + return self.srcdir + return Base.srcnode(self) + + def get_timestamp(self): + """Return the latest timestamp from among our children""" + stamp = 0 + for kid in self.children(): + if kid.get_timestamp() > stamp: + stamp = kid.get_timestamp() + return stamp + + def entry_abspath(self, name): + return self.abspath + OS_SEP + name + + def entry_labspath(self, name): + return self.labspath + '/' + name + + def entry_path(self, name): + return self.path + OS_SEP + name + + def entry_tpath(self, name): + return self.tpath + OS_SEP + name + + def entry_exists_on_disk(self, name): + try: + d = self.on_disk_entries + except AttributeError: + d = {} + try: + entries = os.listdir(self.abspath) + except OSError: + pass + else: + for entry in map(_my_normcase, entries): + d[entry] = True + self.on_disk_entries = d + if sys.platform == 'win32': + name = _my_normcase(name) + result = d.get(name) + if result is None: + # Belt-and-suspenders for Windows: check directly for + # 8.3 file names that don't show up in os.listdir(). + result = os.path.exists(self.abspath + OS_SEP + name) + d[name] = result + return result + else: + return name in d + + memoizer_counters.append(SCons.Memoize.CountValue('srcdir_list')) + + def srcdir_list(self): + try: + return self._memo['srcdir_list'] + except KeyError: + pass + + result = [] + + dirname = '.' + dir = self + while dir: + if dir.srcdir: + result.append(dir.srcdir.Dir(dirname)) + dirname = dir.name + OS_SEP + dirname + dir = dir.up() + + self._memo['srcdir_list'] = result + + return result + + def srcdir_duplicate(self, name): + for dir in self.srcdir_list(): + if self.is_under(dir): + # We shouldn't source from something in the build path; + # variant_dir is probably under src_dir, in which case + # we are reflecting. + break + if dir.entry_exists_on_disk(name): + srcnode = dir.Entry(name).disambiguate() + if self.duplicate: + node = self.Entry(name).disambiguate() + node.do_duplicate(srcnode) + return node + else: + return srcnode + return None + + def _srcdir_find_file_key(self, filename): + return filename + + memoizer_counters.append(SCons.Memoize.CountDict('srcdir_find_file', _srcdir_find_file_key)) + + def srcdir_find_file(self, filename): + try: + memo_dict = self._memo['srcdir_find_file'] + except KeyError: + memo_dict = {} + self._memo['srcdir_find_file'] = memo_dict + else: + try: + return memo_dict[filename] + except KeyError: + pass + + def func(node): + if (isinstance(node, File) or isinstance(node, Entry)) and \ + (node.is_derived() or node.exists()): + return node + return None + + norm_name = _my_normcase(filename) + + for rdir in self.get_all_rdirs(): + try: node = rdir.entries[norm_name] + except KeyError: node = rdir.file_on_disk(filename) + else: node = func(node) + if node: + result = (node, self) + memo_dict[filename] = result + return result + + for srcdir in self.srcdir_list(): + for rdir in srcdir.get_all_rdirs(): + try: node = rdir.entries[norm_name] + except KeyError: node = rdir.file_on_disk(filename) + else: node = func(node) + if node: + result = (File(filename, self, self.fs), srcdir) + memo_dict[filename] = result + return result + + result = (None, None) + memo_dict[filename] = result + return result + + def dir_on_disk(self, name): + if self.entry_exists_on_disk(name): + try: return self.Dir(name) + except TypeError: pass + node = self.srcdir_duplicate(name) + if isinstance(node, File): + return None + return node + + def file_on_disk(self, name): + if self.entry_exists_on_disk(name) or \ + diskcheck_rcs(self, name) or \ + diskcheck_sccs(self, name): + try: return self.File(name) + except TypeError: pass + node = self.srcdir_duplicate(name) + if isinstance(node, Dir): + return None + return node + + def walk(self, func, arg): + """ + Walk this directory tree by calling the specified function + for each directory in the tree. + + This behaves like the os.path.walk() function, but for in-memory + Node.FS.Dir objects. The function takes the same arguments as + the functions passed to os.path.walk(): + + func(arg, dirname, fnames) + + Except that "dirname" will actually be the directory *Node*, + not the string. The '.' and '..' entries are excluded from + fnames. The fnames list may be modified in-place to filter the + subdirectories visited or otherwise impose a specific order. + The "arg" argument is always passed to func() and may be used + in any way (or ignored, passing None is common). + """ + entries = self.entries + names = list(entries.keys()) + names.remove('.') + names.remove('..') + func(arg, self, names) + for dirname in [n for n in names if isinstance(entries[n], Dir)]: + entries[dirname].walk(func, arg) + + def glob(self, pathname, ondisk=True, source=False, strings=False): + """ + Returns a list of Nodes (or strings) matching a specified + pathname pattern. + + Pathname patterns follow UNIX shell semantics: * matches + any-length strings of any characters, ? matches any character, + and [] can enclose lists or ranges of characters. Matches do + not span directory separators. + + The matches take into account Repositories, returning local + Nodes if a corresponding entry exists in a Repository (either + an in-memory Node or something on disk). + + By defafult, the glob() function matches entries that exist + on-disk, in addition to in-memory Nodes. Setting the "ondisk" + argument to False (or some other non-true value) causes the glob() + function to only match in-memory Nodes. The default behavior is + to return both the on-disk and in-memory Nodes. + + The "source" argument, when true, specifies that corresponding + source Nodes must be returned if you're globbing in a build + directory (initialized with VariantDir()). The default behavior + is to return Nodes local to the VariantDir(). + + The "strings" argument, when true, returns the matches as strings, + not Nodes. The strings are path names relative to this directory. + + The underlying algorithm is adapted from the glob.glob() function + in the Python library (but heavily modified), and uses fnmatch() + under the covers. + """ + dirname, basename = os.path.split(pathname) + if not dirname: + return sorted(self._glob1(basename, ondisk, source, strings), + key=lambda t: str(t)) + if has_glob_magic(dirname): + list = self.glob(dirname, ondisk, source, strings=False) + else: + list = [self.Dir(dirname, create=True)] + result = [] + for dir in list: + r = dir._glob1(basename, ondisk, source, strings) + if strings: + r = [os.path.join(str(dir), x) for x in r] + result.extend(r) + return sorted(result, key=lambda a: str(a)) + + def _glob1(self, pattern, ondisk=True, source=False, strings=False): + """ + Globs for and returns a list of entry names matching a single + pattern in this directory. + + This searches any repositories and source directories for + corresponding entries and returns a Node (or string) relative + to the current directory if an entry is found anywhere. + + TODO: handle pattern with no wildcard + """ + search_dir_list = self.get_all_rdirs() + for srcdir in self.srcdir_list(): + search_dir_list.extend(srcdir.get_all_rdirs()) + + selfEntry = self.Entry + names = [] + for dir in search_dir_list: + # We use the .name attribute from the Node because the keys of + # the dir.entries dictionary are normalized (that is, all upper + # case) on case-insensitive systems like Windows. + node_names = [ v.name for k, v in dir.entries.items() + if k not in ('.', '..') ] + names.extend(node_names) + if not strings: + # Make sure the working directory (self) actually has + # entries for all Nodes in repositories or variant dirs. + for name in node_names: selfEntry(name) + if ondisk: + try: + disk_names = os.listdir(dir.abspath) + except os.error: + continue + names.extend(disk_names) + if not strings: + # We're going to return corresponding Nodes in + # the local directory, so we need to make sure + # those Nodes exist. We only want to create + # Nodes for the entries that will match the + # specified pattern, though, which means we + # need to filter the list here, even though + # the overall list will also be filtered later, + # after we exit this loop. + if pattern[0] != '.': + #disk_names = [ d for d in disk_names if d[0] != '.' ] + disk_names = [x for x in disk_names if x[0] != '.'] + disk_names = fnmatch.filter(disk_names, pattern) + dirEntry = dir.Entry + for name in disk_names: + # Add './' before disk filename so that '#' at + # beginning of filename isn't interpreted. + name = './' + name + node = dirEntry(name).disambiguate() + n = selfEntry(name) + if n.__class__ != node.__class__: + n.__class__ = node.__class__ + n._morph() + + names = set(names) + if pattern[0] != '.': + #names = [ n for n in names if n[0] != '.' ] + names = [x for x in names if x[0] != '.'] + names = fnmatch.filter(names, pattern) + + if strings: + return names + + #return [ self.entries[_my_normcase(n)] for n in names ] + return [self.entries[_my_normcase(n)] for n in names] + +class RootDir(Dir): + """A class for the root directory of a file system. + + This is the same as a Dir class, except that the path separator + ('/' or '\\') is actually part of the name, so we don't need to + add a separator when creating the path names of entries within + this directory. + """ + def __init__(self, drive, fs): + if __debug__: logInstanceCreation(self, 'Node.FS.RootDir') + # We're going to be our own parent directory (".." entry and .dir + # attribute) so we have to set up some values so Base.__init__() + # won't gag won't it calls some of our methods. + self.abspath = '' + self.labspath = '' + self.path = '' + self.tpath = '' + self.path_elements = [] + self.duplicate = 0 + self.root = self + + # Handle all the types of drives: + if drive == '': + # No drive, regular UNIX root or Windows default drive. + name = OS_SEP + dirname = OS_SEP + elif drive == '//': + # UNC path + name = UNC_PREFIX + dirname = UNC_PREFIX + else: + # Windows drive letter + name = drive + dirname = drive + OS_SEP + + Base.__init__(self, name, self, fs) + + # Now set our paths to what we really want them to be. The + # name should already contain any necessary separators, such + # as the initial drive letter (the name) plus the directory + # separator, except for the "lookup abspath," which does not + # have the drive letter. + self.abspath = dirname + self.labspath = '' + self.path = dirname + self.tpath = dirname + self._morph() + + # Must be reset after Dir._morph() is invoked... + self.dirname = dirname + + self._lookupDict = {} + + self._lookupDict[''] = self + self._lookupDict['/'] = self + + # The // entry is necessary because os.path.normpath() + # preserves double slashes at the beginning of a path on Posix + # platforms. + if not has_unc: + self._lookupDict['//'] = self + + def must_be_same(self, klass): + if klass is Dir: + return + Base.must_be_same(self, klass) + + def _lookup_abs(self, p, klass, create=1): + """ + Fast (?) lookup of a *normalized* absolute path. + + This method is intended for use by internal lookups with + already-normalized path data. For general-purpose lookups, + use the FS.Entry(), FS.Dir() or FS.File() methods. + + The caller is responsible for making sure we're passed a + normalized absolute path; we merely let Python's dictionary look + up and return the One True Node.FS object for the path. + + If a Node for the specified "p" doesn't already exist, and + "create" is specified, the Node may be created after recursive + invocation to find or create the parent directory or directories. + """ + k = _my_normcase(p) + try: + result = self._lookupDict[k] + except KeyError: + if not create: + msg = "No such file or directory: '%s' in '%s' (and create is False)" % (p, str(self)) + raise SCons.Errors.UserError(msg) + # There is no Node for this path name, and we're allowed + # to create it. + # (note: would like to use p.rsplit('/',1) here but + # that's not in python 2.3) + # e.g.: dir_name, file_name = p.rsplit('/',1) + last_slash = p.rindex('/') + if (last_slash >= 0): + dir_name = p[:last_slash] + file_name = p[last_slash+1:] + else: + dir_name = p # shouldn't happen, just in case + file_name = '' + + dir_node = self._lookup_abs(dir_name, Dir) + result = klass(file_name, dir_node, self.fs) + + # Double-check on disk (as configured) that the Node we + # created matches whatever is out there in the real world. + result.diskcheck_match() + + self._lookupDict[k] = result + dir_node.entries[_my_normcase(file_name)] = result + dir_node.implicit = None + else: + # There is already a Node for this path name. Allow it to + # complain if we were looking for an inappropriate type. + result.must_be_same(klass) + return result + + def __str__(self): + return self.abspath + + def entry_abspath(self, name): + return self.abspath + name + + def entry_labspath(self, name): + return '/' + name + + def entry_path(self, name): + return self.path + name + + def entry_tpath(self, name): + return self.tpath + name + + def is_under(self, dir): + if self is dir: + return 1 + else: + return 0 + + def up(self): + return None + + def get_dir(self): + return None + + def src_builder(self): + return _null + +class FileNodeInfo(SCons.Node.NodeInfoBase): + current_version_id = 1 + + field_list = ['csig', 'timestamp', 'size'] + + # This should get reset by the FS initialization. + fs = None + + def str_to_node(self, s): + top = self.fs.Top + root = top.root + if do_splitdrive: + drive, s = _my_splitdrive(s) + if drive: + root = self.fs.get_root(drive) + if not os.path.isabs(s): + s = top.labspath + '/' + s + return root._lookup_abs(s, Entry) + +class FileBuildInfo(SCons.Node.BuildInfoBase): + current_version_id = 1 + + def convert_to_sconsign(self): + """ + Converts this FileBuildInfo object for writing to a .sconsign file + + This replaces each Node in our various dependency lists with its + usual string representation: relative to the top-level SConstruct + directory, or an absolute path if it's outside. + """ + if os_sep_is_slash: + node_to_str = str + else: + def node_to_str(n): + try: + s = n.path + except AttributeError: + s = str(n) + else: + s = s.replace(OS_SEP, '/') + return s + for attr in ['bsources', 'bdepends', 'bimplicit']: + try: + val = getattr(self, attr) + except AttributeError: + pass + else: + setattr(self, attr, list(map(node_to_str, val))) + def convert_from_sconsign(self, dir, name): + """ + Converts a newly-read FileBuildInfo object for in-SCons use + + For normal up-to-date checking, we don't have any conversion to + perform--but we're leaving this method here to make that clear. + """ + pass + def prepare_dependencies(self): + """ + Prepares a FileBuildInfo object for explaining what changed + + The bsources, bdepends and bimplicit lists have all been + stored on disk as paths relative to the top-level SConstruct + directory. Convert the strings to actual Nodes (for use by the + --debug=explain code and --implicit-cache). + """ + attrs = [ + ('bsources', 'bsourcesigs'), + ('bdepends', 'bdependsigs'), + ('bimplicit', 'bimplicitsigs'), + ] + for (nattr, sattr) in attrs: + try: + strings = getattr(self, nattr) + nodeinfos = getattr(self, sattr) + except AttributeError: + continue + nodes = [] + for s, ni in zip(strings, nodeinfos): + if not isinstance(s, SCons.Node.Node): + s = ni.str_to_node(s) + nodes.append(s) + setattr(self, nattr, nodes) + def format(self, names=0): + result = [] + bkids = self.bsources + self.bdepends + self.bimplicit + bkidsigs = self.bsourcesigs + self.bdependsigs + self.bimplicitsigs + for bkid, bkidsig in zip(bkids, bkidsigs): + result.append(str(bkid) + ': ' + + ' '.join(bkidsig.format(names=names))) + result.append('%s [%s]' % (self.bactsig, self.bact)) + return '\n'.join(result) + +class File(Base): + """A class for files in a file system. + """ + + memoizer_counters = [] + + NodeInfo = FileNodeInfo + BuildInfo = FileBuildInfo + + md5_chunksize = 64 + + def diskcheck_match(self): + diskcheck_match(self, self.isdir, + "Directory %s found where file expected.") + + def __init__(self, name, directory, fs): + if __debug__: logInstanceCreation(self, 'Node.FS.File') + Base.__init__(self, name, directory, fs) + self._morph() + + def Entry(self, name): + """Create an entry node named 'name' relative to + the directory of this file.""" + return self.dir.Entry(name) + + def Dir(self, name, create=True): + """Create a directory node named 'name' relative to + the directory of this file.""" + return self.dir.Dir(name, create=create) + + def Dirs(self, pathlist): + """Create a list of directories relative to the SConscript + directory of this file.""" + return [self.Dir(p) for p in pathlist] + + def File(self, name): + """Create a file node named 'name' relative to + the directory of this file.""" + return self.dir.File(name) + + #def generate_build_dict(self): + # """Return an appropriate dictionary of values for building + # this File.""" + # return {'Dir' : self.Dir, + # 'File' : self.File, + # 'RDirs' : self.RDirs} + + def _morph(self): + """Turn a file system node into a File object.""" + self.scanner_paths = {} + if not hasattr(self, '_local'): + self._local = 0 + + # If there was already a Builder set on this entry, then + # we need to make sure we call the target-decider function, + # not the source-decider. Reaching in and doing this by hand + # is a little bogus. We'd prefer to handle this by adding + # an Entry.builder_set() method that disambiguates like the + # other methods, but that starts running into problems with the + # fragile way we initialize Dir Nodes with their Mkdir builders, + # yet still allow them to be overridden by the user. Since it's + # not clear right now how to fix that, stick with what works + # until it becomes clear... + if self.has_builder(): + self.changed_since_last_build = self.decide_target + + def scanner_key(self): + return self.get_suffix() + + def get_contents(self): + if not self.rexists(): + return '' + fname = self.rfile().abspath + try: + contents = open(fname, "rb").read() + except EnvironmentError, e: + if not e.filename: + e.filename = fname + raise + return contents + + # This attempts to figure out what the encoding of the text is + # based upon the BOM bytes, and then decodes the contents so that + # it's a valid python string. + def get_text_contents(self): + contents = self.get_contents() + # The behavior of various decode() methods and functions + # w.r.t. the initial BOM bytes is different for different + # encodings and/or Python versions. ('utf-8' does not strip + # them, but has a 'utf-8-sig' which does; 'utf-16' seems to + # strip them; etc.) Just sidestep all the complication by + # explicitly stripping the BOM before we decode(). + if contents.startswith(codecs.BOM_UTF8): + return contents[len(codecs.BOM_UTF8):].decode('utf-8') + if contents.startswith(codecs.BOM_UTF16_LE): + return contents[len(codecs.BOM_UTF16_LE):].decode('utf-16-le') + if contents.startswith(codecs.BOM_UTF16_BE): + return contents[len(codecs.BOM_UTF16_BE):].decode('utf-16-be') + return contents + + def get_content_hash(self): + """ + Compute and return the MD5 hash for this file. + """ + if not self.rexists(): + return SCons.Util.MD5signature('') + fname = self.rfile().abspath + try: + cs = SCons.Util.MD5filesignature(fname, + chunksize=SCons.Node.FS.File.md5_chunksize*1024) + except EnvironmentError, e: + if not e.filename: + e.filename = fname + raise + return cs + + + memoizer_counters.append(SCons.Memoize.CountValue('get_size')) + + def get_size(self): + try: + return self._memo['get_size'] + except KeyError: + pass + + if self.rexists(): + size = self.rfile().getsize() + else: + size = 0 + + self._memo['get_size'] = size + + return size + + memoizer_counters.append(SCons.Memoize.CountValue('get_timestamp')) + + def get_timestamp(self): + try: + return self._memo['get_timestamp'] + except KeyError: + pass + + if self.rexists(): + timestamp = self.rfile().getmtime() + else: + timestamp = 0 + + self._memo['get_timestamp'] = timestamp + + return timestamp + + def store_info(self): + # Merge our build information into the already-stored entry. + # This accomodates "chained builds" where a file that's a target + # in one build (SConstruct file) is a source in a different build. + # See test/chained-build.py for the use case. + if do_store_info: + self.dir.sconsign().store_info(self.name, self) + + convert_copy_attrs = [ + 'bsources', + 'bimplicit', + 'bdepends', + 'bact', + 'bactsig', + 'ninfo', + ] + + + convert_sig_attrs = [ + 'bsourcesigs', + 'bimplicitsigs', + 'bdependsigs', + ] + + def convert_old_entry(self, old_entry): + # Convert a .sconsign entry from before the Big Signature + # Refactoring, doing what we can to convert its information + # to the new .sconsign entry format. + # + # The old format looked essentially like this: + # + # BuildInfo + # .ninfo (NodeInfo) + # .bsig + # .csig + # .timestamp + # .size + # .bsources + # .bsourcesigs ("signature" list) + # .bdepends + # .bdependsigs ("signature" list) + # .bimplicit + # .bimplicitsigs ("signature" list) + # .bact + # .bactsig + # + # The new format looks like this: + # + # .ninfo (NodeInfo) + # .bsig + # .csig + # .timestamp + # .size + # .binfo (BuildInfo) + # .bsources + # .bsourcesigs (NodeInfo list) + # .bsig + # .csig + # .timestamp + # .size + # .bdepends + # .bdependsigs (NodeInfo list) + # .bsig + # .csig + # .timestamp + # .size + # .bimplicit + # .bimplicitsigs (NodeInfo list) + # .bsig + # .csig + # .timestamp + # .size + # .bact + # .bactsig + # + # The basic idea of the new structure is that a NodeInfo always + # holds all available information about the state of a given Node + # at a certain point in time. The various .b*sigs lists can just + # be a list of pointers to the .ninfo attributes of the different + # dependent nodes, without any copying of information until it's + # time to pickle it for writing out to a .sconsign file. + # + # The complicating issue is that the *old* format only stored one + # "signature" per dependency, based on however the *last* build + # was configured. We don't know from just looking at it whether + # it was a build signature, a content signature, or a timestamp + # "signature". Since we no longer use build signatures, the + # best we can do is look at the length and if it's thirty two, + # assume that it was (or might have been) a content signature. + # If it was actually a build signature, then it will cause a + # rebuild anyway when it doesn't match the new content signature, + # but that's probably the best we can do. + import SCons.SConsign + new_entry = SCons.SConsign.SConsignEntry() + new_entry.binfo = self.new_binfo() + binfo = new_entry.binfo + for attr in self.convert_copy_attrs: + try: + value = getattr(old_entry, attr) + except AttributeError: + continue + setattr(binfo, attr, value) + delattr(old_entry, attr) + for attr in self.convert_sig_attrs: + try: + sig_list = getattr(old_entry, attr) + except AttributeError: + continue + value = [] + for sig in sig_list: + ninfo = self.new_ninfo() + if len(sig) == 32: + ninfo.csig = sig + else: + ninfo.timestamp = sig + value.append(ninfo) + setattr(binfo, attr, value) + delattr(old_entry, attr) + return new_entry + + memoizer_counters.append(SCons.Memoize.CountValue('get_stored_info')) + + def get_stored_info(self): + try: + return self._memo['get_stored_info'] + except KeyError: + pass + + try: + sconsign_entry = self.dir.sconsign().get_entry(self.name) + except (KeyError, EnvironmentError): + import SCons.SConsign + sconsign_entry = SCons.SConsign.SConsignEntry() + sconsign_entry.binfo = self.new_binfo() + sconsign_entry.ninfo = self.new_ninfo() + else: + if isinstance(sconsign_entry, FileBuildInfo): + # This is a .sconsign file from before the Big Signature + # Refactoring; convert it as best we can. + sconsign_entry = self.convert_old_entry(sconsign_entry) + try: + delattr(sconsign_entry.ninfo, 'bsig') + except AttributeError: + pass + + self._memo['get_stored_info'] = sconsign_entry + + return sconsign_entry + + def get_stored_implicit(self): + binfo = self.get_stored_info().binfo + binfo.prepare_dependencies() + try: return binfo.bimplicit + except AttributeError: return None + + def rel_path(self, other): + return self.dir.rel_path(other) + + def _get_found_includes_key(self, env, scanner, path): + return (id(env), id(scanner), path) + + memoizer_counters.append(SCons.Memoize.CountDict('get_found_includes', _get_found_includes_key)) + + def get_found_includes(self, env, scanner, path): + """Return the included implicit dependencies in this file. + Cache results so we only scan the file once per path + regardless of how many times this information is requested. + """ + memo_key = (id(env), id(scanner), path) + try: + memo_dict = self._memo['get_found_includes'] + except KeyError: + memo_dict = {} + self._memo['get_found_includes'] = memo_dict + else: + try: + return memo_dict[memo_key] + except KeyError: + pass + + if scanner: + # result = [n.disambiguate() for n in scanner(self, env, path)] + result = scanner(self, env, path) + result = [N.disambiguate() for N in result] + else: + result = [] + + memo_dict[memo_key] = result + + return result + + def _createDir(self): + # ensure that the directories for this node are + # created. + self.dir._create() + + def push_to_cache(self): + """Try to push the node into a cache + """ + # This should get called before the Nodes' .built() method is + # called, which would clear the build signature if the file has + # a source scanner. + # + # We have to clear the local memoized values *before* we push + # the node to cache so that the memoization of the self.exists() + # return value doesn't interfere. + if self.nocache: + return + self.clear_memoized_values() + if self.exists(): + self.get_build_env().get_CacheDir().push(self) + + def retrieve_from_cache(self): + """Try to retrieve the node's content from a cache + + This method is called from multiple threads in a parallel build, + so only do thread safe stuff here. Do thread unsafe stuff in + built(). + + Returns true if the node was successfully retrieved. + """ + if self.nocache: + return None + if not self.is_derived(): + return None + return self.get_build_env().get_CacheDir().retrieve(self) + + def visited(self): + if self.exists(): + self.get_build_env().get_CacheDir().push_if_forced(self) + + ninfo = self.get_ninfo() + + csig = self.get_max_drift_csig() + if csig: + ninfo.csig = csig + + ninfo.timestamp = self.get_timestamp() + ninfo.size = self.get_size() + + if not self.has_builder(): + # This is a source file, but it might have been a target file + # in another build that included more of the DAG. Copy + # any build information that's stored in the .sconsign file + # into our binfo object so it doesn't get lost. + old = self.get_stored_info() + self.get_binfo().__dict__.update(old.binfo.__dict__) + + self.store_info() + + def find_src_builder(self): + if self.rexists(): + return None + scb = self.dir.src_builder() + if scb is _null: + if diskcheck_sccs(self.dir, self.name): + scb = get_DefaultSCCSBuilder() + elif diskcheck_rcs(self.dir, self.name): + scb = get_DefaultRCSBuilder() + else: + scb = None + if scb is not None: + try: + b = self.builder + except AttributeError: + b = None + if b is None: + self.builder_set(scb) + return scb + + def has_src_builder(self): + """Return whether this Node has a source builder or not. + + If this Node doesn't have an explicit source code builder, this + is where we figure out, on the fly, if there's a transparent + source code builder for it. + + Note that if we found a source builder, we also set the + self.builder attribute, so that all of the methods that actually + *build* this file don't have to do anything different. + """ + try: + scb = self.sbuilder + except AttributeError: + scb = self.sbuilder = self.find_src_builder() + return scb is not None + + def alter_targets(self): + """Return any corresponding targets in a variant directory. + """ + if self.is_derived(): + return [], None + return self.fs.variant_dir_target_climb(self, self.dir, [self.name]) + + def _rmv_existing(self): + self.clear_memoized_values() + if print_duplicate: + print "dup: removing existing target %s"%self + e = Unlink(self, [], None) + if isinstance(e, SCons.Errors.BuildError): + raise e + + # + # Taskmaster interface subsystem + # + + def make_ready(self): + self.has_src_builder() + self.get_binfo() + + def prepare(self): + """Prepare for this file to be created.""" + SCons.Node.Node.prepare(self) + + if self.get_state() != SCons.Node.up_to_date: + if self.exists(): + if self.is_derived() and not self.precious: + self._rmv_existing() + else: + try: + self._createDir() + except SCons.Errors.StopError, drive: + desc = "No drive `%s' for target `%s'." % (drive, self) + raise SCons.Errors.StopError(desc) + + # + # + # + + def remove(self): + """Remove this file.""" + if self.exists() or self.islink(): + self.fs.unlink(self.path) + return 1 + return None + + def do_duplicate(self, src): + self._createDir() + if print_duplicate: + print "dup: relinking variant '%s' from '%s'"%(self, src) + Unlink(self, None, None) + e = Link(self, src, None) + if isinstance(e, SCons.Errors.BuildError): + desc = "Cannot duplicate `%s' in `%s': %s." % (src.path, self.dir.path, e.errstr) + raise SCons.Errors.StopError(desc) + self.linked = 1 + # The Link() action may or may not have actually + # created the file, depending on whether the -n + # option was used or not. Delete the _exists and + # _rexists attributes so they can be reevaluated. + self.clear() + + memoizer_counters.append(SCons.Memoize.CountValue('exists')) + + def exists(self): + try: + return self._memo['exists'] + except KeyError: + pass + # Duplicate from source path if we are set up to do this. + if self.duplicate and not self.is_derived() and not self.linked: + src = self.srcnode() + if src is not self: + # At this point, src is meant to be copied in a variant directory. + src = src.rfile() + if src.abspath != self.abspath: + if src.exists(): + self.do_duplicate(src) + # Can't return 1 here because the duplication might + # not actually occur if the -n option is being used. + else: + # The source file does not exist. Make sure no old + # copy remains in the variant directory. + if print_duplicate: + print "dup: no src for %s, unlinking old variant copy"%self + if Base.exists(self) or self.islink(): + self.fs.unlink(self.path) + # Return None explicitly because the Base.exists() call + # above will have cached its value if the file existed. + self._memo['exists'] = None + return None + result = Base.exists(self) + self._memo['exists'] = result + return result + + # + # SIGNATURE SUBSYSTEM + # + + def get_max_drift_csig(self): + """ + Returns the content signature currently stored for this node + if it's been unmodified longer than the max_drift value, or the + max_drift value is 0. Returns None otherwise. + """ + old = self.get_stored_info() + mtime = self.get_timestamp() + + max_drift = self.fs.max_drift + if max_drift > 0: + if (time.time() - mtime) > max_drift: + try: + n = old.ninfo + if n.timestamp and n.csig and n.timestamp == mtime: + return n.csig + except AttributeError: + pass + elif max_drift == 0: + try: + return old.ninfo.csig + except AttributeError: + pass + + return None + + def get_csig(self): + """ + Generate a node's content signature, the digested signature + of its content. + + node - the node + cache - alternate node to use for the signature cache + returns - the content signature + """ + ninfo = self.get_ninfo() + try: + return ninfo.csig + except AttributeError: + pass + + csig = self.get_max_drift_csig() + if csig is None: + + try: + if self.get_size() < SCons.Node.FS.File.md5_chunksize: + contents = self.get_contents() + else: + csig = self.get_content_hash() + except IOError: + # This can happen if there's actually a directory on-disk, + # which can be the case if they've disabled disk checks, + # or if an action with a File target actually happens to + # create a same-named directory by mistake. + csig = '' + else: + if not csig: + csig = SCons.Util.MD5signature(contents) + + ninfo.csig = csig + + return csig + + # + # DECISION SUBSYSTEM + # + + def builder_set(self, builder): + SCons.Node.Node.builder_set(self, builder) + self.changed_since_last_build = self.decide_target + + def changed_content(self, target, prev_ni): + cur_csig = self.get_csig() + try: + return cur_csig != prev_ni.csig + except AttributeError: + return 1 + + def changed_state(self, target, prev_ni): + return self.state != SCons.Node.up_to_date + + def changed_timestamp_then_content(self, target, prev_ni): + if not self.changed_timestamp_match(target, prev_ni): + try: + self.get_ninfo().csig = prev_ni.csig + except AttributeError: + pass + return False + return self.changed_content(target, prev_ni) + + def changed_timestamp_newer(self, target, prev_ni): + try: + return self.get_timestamp() > target.get_timestamp() + except AttributeError: + return 1 + + def changed_timestamp_match(self, target, prev_ni): + try: + return self.get_timestamp() != prev_ni.timestamp + except AttributeError: + return 1 + + def decide_source(self, target, prev_ni): + return target.get_build_env().decide_source(self, target, prev_ni) + + def decide_target(self, target, prev_ni): + return target.get_build_env().decide_target(self, target, prev_ni) + + # Initialize this Node's decider function to decide_source() because + # every file is a source file until it has a Builder attached... + changed_since_last_build = decide_source + + def is_up_to_date(self): + T = 0 + if T: Trace('is_up_to_date(%s):' % self) + if not self.exists(): + if T: Trace(' not self.exists():') + # The file doesn't exist locally... + r = self.rfile() + if r != self: + # ...but there is one in a Repository... + if not self.changed(r): + if T: Trace(' changed(%s):' % r) + # ...and it's even up-to-date... + if self._local: + # ...and they'd like a local copy. + e = LocalCopy(self, r, None) + if isinstance(e, SCons.Errors.BuildError): + raise + self.store_info() + if T: Trace(' 1\n') + return 1 + self.changed() + if T: Trace(' None\n') + return None + else: + r = self.changed() + if T: Trace(' self.exists(): %s\n' % r) + return not r + + memoizer_counters.append(SCons.Memoize.CountValue('rfile')) + + def rfile(self): + try: + return self._memo['rfile'] + except KeyError: + pass + result = self + if not self.exists(): + norm_name = _my_normcase(self.name) + for dir in self.dir.get_all_rdirs(): + try: node = dir.entries[norm_name] + except KeyError: node = dir.file_on_disk(self.name) + if node and node.exists() and \ + (isinstance(node, File) or isinstance(node, Entry) \ + or not node.is_derived()): + result = node + # Copy over our local attributes to the repository + # Node so we identify shared object files in the + # repository and don't assume they're static. + # + # This isn't perfect; the attribute would ideally + # be attached to the object in the repository in + # case it was built statically in the repository + # and we changed it to shared locally, but that's + # rarely the case and would only occur if you + # intentionally used the same suffix for both + # shared and static objects anyway. So this + # should work well in practice. + result.attributes = self.attributes + break + self._memo['rfile'] = result + return result + + def rstr(self): + return str(self.rfile()) + + def get_cachedir_csig(self): + """ + Fetch a Node's content signature for purposes of computing + another Node's cachesig. + + This is a wrapper around the normal get_csig() method that handles + the somewhat obscure case of using CacheDir with the -n option. + Any files that don't exist would normally be "built" by fetching + them from the cache, but the normal get_csig() method will try + to open up the local file, which doesn't exist because the -n + option meant we didn't actually pull the file from cachedir. + But since the file *does* actually exist in the cachedir, we + can use its contents for the csig. + """ + try: + return self.cachedir_csig + except AttributeError: + pass + + cachedir, cachefile = self.get_build_env().get_CacheDir().cachepath(self) + if not self.exists() and cachefile and os.path.exists(cachefile): + self.cachedir_csig = SCons.Util.MD5filesignature(cachefile, \ + SCons.Node.FS.File.md5_chunksize * 1024) + else: + self.cachedir_csig = self.get_csig() + return self.cachedir_csig + + def get_cachedir_bsig(self): + try: + return self.cachesig + except AttributeError: + pass + + # Add the path to the cache signature, because multiple + # targets built by the same action will all have the same + # build signature, and we have to differentiate them somehow. + children = self.children() + executor = self.get_executor() + # sigs = [n.get_cachedir_csig() for n in children] + sigs = [n.get_cachedir_csig() for n in children] + sigs.append(SCons.Util.MD5signature(executor.get_contents())) + sigs.append(self.path) + result = self.cachesig = SCons.Util.MD5collect(sigs) + return result + + +default_fs = None + +def get_default_fs(): + global default_fs + if not default_fs: + default_fs = FS() + return default_fs + +class FileFinder(object): + """ + """ + if SCons.Memoize.use_memoizer: + __metaclass__ = SCons.Memoize.Memoized_Metaclass + + memoizer_counters = [] + + def __init__(self): + self._memo = {} + + def filedir_lookup(self, p, fd=None): + """ + A helper method for find_file() that looks up a directory for + a file we're trying to find. This only creates the Dir Node if + it exists on-disk, since if the directory doesn't exist we know + we won't find any files in it... :-) + + It would be more compact to just use this as a nested function + with a default keyword argument (see the commented-out version + below), but that doesn't work unless you have nested scopes, + so we define it here just so this work under Python 1.5.2. + """ + if fd is None: + fd = self.default_filedir + dir, name = os.path.split(fd) + drive, d = _my_splitdrive(dir) + if not name and d[:1] in ('/', OS_SEP): + #return p.fs.get_root(drive).dir_on_disk(name) + return p.fs.get_root(drive) + if dir: + p = self.filedir_lookup(p, dir) + if not p: + return None + norm_name = _my_normcase(name) + try: + node = p.entries[norm_name] + except KeyError: + return p.dir_on_disk(name) + if isinstance(node, Dir): + return node + if isinstance(node, Entry): + node.must_be_same(Dir) + return node + return None + + def _find_file_key(self, filename, paths, verbose=None): + return (filename, paths) + + memoizer_counters.append(SCons.Memoize.CountDict('find_file', _find_file_key)) + + def find_file(self, filename, paths, verbose=None): + """ + find_file(str, [Dir()]) -> [nodes] + + filename - a filename to find + paths - a list of directory path *nodes* to search in. Can be + represented as a list, a tuple, or a callable that is + called with no arguments and returns the list or tuple. + + returns - the node created from the found file. + + Find a node corresponding to either a derived file or a file + that exists already. + + Only the first file found is returned, and none is returned + if no file is found. + """ + memo_key = self._find_file_key(filename, paths) + try: + memo_dict = self._memo['find_file'] + except KeyError: + memo_dict = {} + self._memo['find_file'] = memo_dict + else: + try: + return memo_dict[memo_key] + except KeyError: + pass + + if verbose and not callable(verbose): + if not SCons.Util.is_String(verbose): + verbose = "find_file" + _verbose = u' %s: ' % verbose + verbose = lambda s: sys.stdout.write(_verbose + s) + + filedir, filename = os.path.split(filename) + if filedir: + # More compact code that we can't use until we drop + # support for Python 1.5.2: + # + #def filedir_lookup(p, fd=filedir): + # """ + # A helper function that looks up a directory for a file + # we're trying to find. This only creates the Dir Node + # if it exists on-disk, since if the directory doesn't + # exist we know we won't find any files in it... :-) + # """ + # dir, name = os.path.split(fd) + # if dir: + # p = filedir_lookup(p, dir) + # if not p: + # return None + # norm_name = _my_normcase(name) + # try: + # node = p.entries[norm_name] + # except KeyError: + # return p.dir_on_disk(name) + # if isinstance(node, Dir): + # return node + # if isinstance(node, Entry): + # node.must_be_same(Dir) + # return node + # if isinstance(node, Dir) or isinstance(node, Entry): + # return node + # return None + #paths = [_f for _f in map(filedir_lookup, paths) if _f] + + self.default_filedir = filedir + paths = [_f for _f in map(self.filedir_lookup, paths) if _f] + + result = None + for dir in paths: + if verbose: + verbose("looking for '%s' in '%s' ...\n" % (filename, dir)) + node, d = dir.srcdir_find_file(filename) + if node: + if verbose: + verbose("... FOUND '%s' in '%s'\n" % (filename, d)) + result = node + break + + memo_dict[memo_key] = result + + return result + +find_file = FileFinder().find_file + + +def invalidate_node_memos(targets): + """ + Invalidate the memoized values of all Nodes (files or directories) + that are associated with the given entries. Has been added to + clear the cache of nodes affected by a direct execution of an + action (e.g. Delete/Copy/Chmod). Existing Node caches become + inconsistent if the action is run through Execute(). The argument + `targets` can be a single Node object or filename, or a sequence + of Nodes/filenames. + """ + from traceback import extract_stack + + # First check if the cache really needs to be flushed. Only + # actions run in the SConscript with Execute() seem to be + # affected. XXX The way to check if Execute() is in the stacktrace + # is a very dirty hack and should be replaced by a more sensible + # solution. + for f in extract_stack(): + if f[2] == 'Execute' and f[0][-14:] == 'Environment.py': + break + else: + # Dont have to invalidate, so return + return + + if not SCons.Util.is_List(targets): + targets = [targets] + + for entry in targets: + # If the target is a Node object, clear the cache. If it is a + # filename, look up potentially existing Node object first. + try: + entry.clear_memoized_values() + except AttributeError: + # Not a Node object, try to look up Node by filename. XXX + # This creates Node objects even for those filenames which + # do not correspond to an existing Node object. + node = get_default_fs().Entry(entry) + if node: + node.clear_memoized_values() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Node/Python.py b/scons/scons-local-2.3.0/SCons/Node/Python.py new file mode 100644 index 000000000..153e32d0b --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Node/Python.py @@ -0,0 +1,128 @@ +"""scons.Node.Python + +Python nodes. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Node/Python.py 2013/03/03 09:48:35 garyo" + +import SCons.Node + +class ValueNodeInfo(SCons.Node.NodeInfoBase): + current_version_id = 1 + + field_list = ['csig'] + + def str_to_node(self, s): + return Value(s) + +class ValueBuildInfo(SCons.Node.BuildInfoBase): + current_version_id = 1 + +class Value(SCons.Node.Node): + """A class for Python variables, typically passed on the command line + or generated by a script, but not from a file or some other source. + """ + + NodeInfo = ValueNodeInfo + BuildInfo = ValueBuildInfo + + def __init__(self, value, built_value=None): + SCons.Node.Node.__init__(self) + self.value = value + if built_value is not None: + self.built_value = built_value + + def str_for_display(self): + return repr(self.value) + + def __str__(self): + return str(self.value) + + def make_ready(self): + self.get_csig() + + def build(self, **kw): + if not hasattr(self, 'built_value'): + SCons.Node.Node.build(self, **kw) + + is_up_to_date = SCons.Node.Node.children_are_up_to_date + + def is_under(self, dir): + # Make Value nodes get built regardless of + # what directory scons was run from. Value nodes + # are outside the filesystem: + return 1 + + def write(self, built_value): + """Set the value of the node.""" + self.built_value = built_value + + def read(self): + """Return the value. If necessary, the value is built.""" + self.build() + if not hasattr(self, 'built_value'): + self.built_value = self.value + return self.built_value + + def get_text_contents(self): + """By the assumption that the node.built_value is a + deterministic product of the sources, the contents of a Value + are the concatenation of all the contents of its sources. As + the value need not be built when get_contents() is called, we + cannot use the actual node.built_value.""" + ###TODO: something reasonable about universal newlines + contents = str(self.value) + for kid in self.children(None): + contents = contents + kid.get_contents() + return contents + + get_contents = get_text_contents ###TODO should return 'bytes' value + + def changed_since_last_build(self, target, prev_ni): + cur_csig = self.get_csig() + try: + return cur_csig != prev_ni.csig + except AttributeError: + return 1 + + def get_csig(self, calc=None): + """Because we're a Python value node and don't have a real + timestamp, we get to ignore the calculator and just use the + value contents.""" + try: + return self.ninfo.csig + except AttributeError: + pass + contents = self.get_contents() + self.get_ninfo().csig = contents + return contents + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Node/__init__.py b/scons/scons-local-2.3.0/SCons/Node/__init__.py new file mode 100644 index 000000000..ece4a5a44 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Node/__init__.py @@ -0,0 +1,1330 @@ +"""SCons.Node + +The Node package for the SCons software construction utility. + +This is, in many ways, the heart of SCons. + +A Node is where we encapsulate all of the dependency information about +any thing that SCons can build, or about any thing which SCons can use +to build some other thing. The canonical "thing," of course, is a file, +but a Node can also represent something remote (like a web page) or +something completely abstract (like an Alias). + +Each specific type of "thing" is specifically represented by a subclass +of the Node base class: Node.FS.File for files, Node.Alias for aliases, +etc. Dependency information is kept here in the base class, and +information specific to files/aliases/etc. is in the subclass. The +goal, if we've done this correctly, is that any type of "thing" should +be able to depend on any other type of "thing." + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Node/__init__.py 2013/03/03 09:48:35 garyo" + +import collections +import copy +from itertools import chain + +from SCons.Debug import logInstanceCreation +import SCons.Executor +import SCons.Memoize +import SCons.Util + +from SCons.Debug import Trace + +def classname(obj): + return str(obj.__class__).split('.')[-1] + +# Node states +# +# These are in "priority" order, so that the maximum value for any +# child/dependency of a node represents the state of that node if +# it has no builder of its own. The canonical example is a file +# system directory, which is only up to date if all of its children +# were up to date. +no_state = 0 +pending = 1 +executing = 2 +up_to_date = 3 +executed = 4 +failed = 5 + +StateString = { + 0 : "no_state", + 1 : "pending", + 2 : "executing", + 3 : "up_to_date", + 4 : "executed", + 5 : "failed", +} + +# controls whether implicit dependencies are cached: +implicit_cache = 0 + +# controls whether implicit dep changes are ignored: +implicit_deps_unchanged = 0 + +# controls whether the cached implicit deps are ignored: +implicit_deps_changed = 0 + +# A variable that can be set to an interface-specific function be called +# to annotate a Node with information about its creation. +def do_nothing(node): pass + +Annotate = do_nothing + +# Classes for signature info for Nodes. + +class NodeInfoBase(object): + """ + The generic base class for signature information for a Node. + + Node subclasses should subclass NodeInfoBase to provide their own + logic for dealing with their own Node-specific signature information. + """ + current_version_id = 1 + def __init__(self, node=None): + # Create an object attribute from the class attribute so it ends up + # in the pickled data in the .sconsign file. + self._version_id = self.current_version_id + def update(self, node): + try: + field_list = self.field_list + except AttributeError: + return + for f in field_list: + try: + delattr(self, f) + except AttributeError: + pass + try: + func = getattr(node, 'get_' + f) + except AttributeError: + pass + else: + setattr(self, f, func()) + def convert(self, node, val): + pass + def merge(self, other): + self.__dict__.update(other.__dict__) + def format(self, field_list=None, names=0): + if field_list is None: + try: + field_list = self.field_list + except AttributeError: + field_list = sorted(self.__dict__.keys()) + fields = [] + for field in field_list: + try: + f = getattr(self, field) + except AttributeError: + f = None + f = str(f) + if names: + f = field + ': ' + f + fields.append(f) + return fields + +class BuildInfoBase(object): + """ + The generic base class for build information for a Node. + + This is what gets stored in a .sconsign file for each target file. + It contains a NodeInfo instance for this node (signature information + that's specific to the type of Node) and direct attributes for the + generic build stuff we have to track: sources, explicit dependencies, + implicit dependencies, and action information. + """ + current_version_id = 1 + def __init__(self, node=None): + # Create an object attribute from the class attribute so it ends up + # in the pickled data in the .sconsign file. + self._version_id = self.current_version_id + self.bsourcesigs = [] + self.bdependsigs = [] + self.bimplicitsigs = [] + self.bactsig = None + def merge(self, other): + self.__dict__.update(other.__dict__) + +class Node(object): + """The base Node class, for entities that we know how to + build, or use to build other Nodes. + """ + + if SCons.Memoize.use_memoizer: + __metaclass__ = SCons.Memoize.Memoized_Metaclass + + memoizer_counters = [] + + class Attrs(object): + pass + + def __init__(self): + if __debug__: logInstanceCreation(self, 'Node.Node') + # Note that we no longer explicitly initialize a self.builder + # attribute to None here. That's because the self.builder + # attribute may be created on-the-fly later by a subclass (the + # canonical example being a builder to fetch a file from a + # source code system like CVS or Subversion). + + # Each list of children that we maintain is accompanied by a + # dictionary used to look up quickly whether a node is already + # present in the list. Empirical tests showed that it was + # fastest to maintain them as side-by-side Node attributes in + # this way, instead of wrapping up each list+dictionary pair in + # a class. (Of course, we could always still do that in the + # future if we had a good reason to...). + self.sources = [] # source files used to build node + self.sources_set = set() + self._specific_sources = False + self.depends = [] # explicit dependencies (from Depends) + self.depends_set = set() + self.ignore = [] # dependencies to ignore + self.ignore_set = set() + self.prerequisites = SCons.Util.UniqueList() + self.implicit = None # implicit (scanned) dependencies (None means not scanned yet) + self.waiting_parents = set() + self.waiting_s_e = set() + self.ref_count = 0 + self.wkids = None # Kids yet to walk, when it's an array + + self.env = None + self.state = no_state + self.precious = None + self.noclean = 0 + self.nocache = 0 + self.cached = 0 # is this node pulled from cache? + self.always_build = None + self.includes = None + self.attributes = self.Attrs() # Generic place to stick information about the Node. + self.side_effect = 0 # true iff this node is a side effect + self.side_effects = [] # the side effects of building this target + self.linked = 0 # is this node linked to the variant directory? + + self.clear_memoized_values() + + # Let the interface in which the build engine is embedded + # annotate this Node with its own info (like a description of + # what line in what file created the node, for example). + Annotate(self) + + def disambiguate(self, must_exist=None): + return self + + def get_suffix(self): + return '' + + memoizer_counters.append(SCons.Memoize.CountValue('get_build_env')) + + def get_build_env(self): + """Fetch the appropriate Environment to build this node. + """ + try: + return self._memo['get_build_env'] + except KeyError: + pass + result = self.get_executor().get_build_env() + self._memo['get_build_env'] = result + return result + + def get_build_scanner_path(self, scanner): + """Fetch the appropriate scanner path for this node.""" + return self.get_executor().get_build_scanner_path(scanner) + + def set_executor(self, executor): + """Set the action executor for this node.""" + self.executor = executor + + def get_executor(self, create=1): + """Fetch the action executor for this node. Create one if + there isn't already one, and requested to do so.""" + try: + executor = self.executor + except AttributeError: + if not create: + raise + try: + act = self.builder.action + except AttributeError: + executor = SCons.Executor.Null(targets=[self]) + else: + executor = SCons.Executor.Executor(act, + self.env or self.builder.env, + [self.builder.overrides], + [self], + self.sources) + self.executor = executor + return executor + + def executor_cleanup(self): + """Let the executor clean up any cached information.""" + try: + executor = self.get_executor(create=None) + except AttributeError: + pass + else: + executor.cleanup() + + def reset_executor(self): + "Remove cached executor; forces recompute when needed." + try: + delattr(self, 'executor') + except AttributeError: + pass + + def push_to_cache(self): + """Try to push a node into a cache + """ + pass + + def retrieve_from_cache(self): + """Try to retrieve the node's content from a cache + + This method is called from multiple threads in a parallel build, + so only do thread safe stuff here. Do thread unsafe stuff in + built(). + + Returns true if the node was successfully retrieved. + """ + return 0 + + # + # Taskmaster interface subsystem + # + + def make_ready(self): + """Get a Node ready for evaluation. + + This is called before the Taskmaster decides if the Node is + up-to-date or not. Overriding this method allows for a Node + subclass to be disambiguated if necessary, or for an implicit + source builder to be attached. + """ + pass + + def prepare(self): + """Prepare for this Node to be built. + + This is called after the Taskmaster has decided that the Node + is out-of-date and must be rebuilt, but before actually calling + the method to build the Node. + + This default implementation checks that explicit or implicit + dependencies either exist or are derived, and initializes the + BuildInfo structure that will hold the information about how + this node is, uh, built. + + (The existence of source files is checked separately by the + Executor, which aggregates checks for all of the targets built + by a specific action.) + + Overriding this method allows for for a Node subclass to remove + the underlying file from the file system. Note that subclass + methods should call this base class method to get the child + check and the BuildInfo structure. + """ + for d in self.depends: + if d.missing(): + msg = "Explicit dependency `%s' not found, needed by target `%s'." + raise SCons.Errors.StopError(msg % (d, self)) + if self.implicit is not None: + for i in self.implicit: + if i.missing(): + msg = "Implicit dependency `%s' not found, needed by target `%s'." + raise SCons.Errors.StopError(msg % (i, self)) + self.binfo = self.get_binfo() + + def build(self, **kw): + """Actually build the node. + + This is called by the Taskmaster after it's decided that the + Node is out-of-date and must be rebuilt, and after the prepare() + method has gotten everything, uh, prepared. + + This method is called from multiple threads in a parallel build, + so only do thread safe stuff here. Do thread unsafe stuff + in built(). + + """ + try: + self.get_executor()(self, **kw) + except SCons.Errors.BuildError, e: + e.node = self + raise + + def built(self): + """Called just after this node is successfully built.""" + + # Clear the implicit dependency caches of any Nodes + # waiting for this Node to be built. + for parent in self.waiting_parents: + parent.implicit = None + + self.clear() + + self.ninfo.update(self) + + def visited(self): + """Called just after this node has been visited (with or + without a build).""" + try: + binfo = self.binfo + except AttributeError: + # Apparently this node doesn't need build info, so + # don't bother calculating or storing it. + pass + else: + self.ninfo.update(self) + self.store_info() + + # + # + # + + def add_to_waiting_s_e(self, node): + self.waiting_s_e.add(node) + + def add_to_waiting_parents(self, node): + """ + Returns the number of nodes added to our waiting parents list: + 1 if we add a unique waiting parent, 0 if not. (Note that the + returned values are intended to be used to increment a reference + count, so don't think you can "clean up" this function by using + True and False instead...) + """ + wp = self.waiting_parents + if node in wp: + return 0 + wp.add(node) + return 1 + + def postprocess(self): + """Clean up anything we don't need to hang onto after we've + been built.""" + self.executor_cleanup() + self.waiting_parents = set() + + def clear(self): + """Completely clear a Node of all its cached state (so that it + can be re-evaluated by interfaces that do continuous integration + builds). + """ + # The del_binfo() call here isn't necessary for normal execution, + # but is for interactive mode, where we might rebuild the same + # target and need to start from scratch. + self.del_binfo() + self.clear_memoized_values() + self.ninfo = self.new_ninfo() + self.executor_cleanup() + try: + delattr(self, '_calculated_sig') + except AttributeError: + pass + self.includes = None + + def clear_memoized_values(self): + self._memo = {} + + def builder_set(self, builder): + self.builder = builder + try: + del self.executor + except AttributeError: + pass + + def has_builder(self): + """Return whether this Node has a builder or not. + + In Boolean tests, this turns out to be a *lot* more efficient + than simply examining the builder attribute directly ("if + node.builder: ..."). When the builder attribute is examined + directly, it ends up calling __getattr__ for both the __len__ + and __nonzero__ attributes on instances of our Builder Proxy + class(es), generating a bazillion extra calls and slowing + things down immensely. + """ + try: + b = self.builder + except AttributeError: + # There was no explicit builder for this Node, so initialize + # the self.builder attribute to None now. + b = self.builder = None + return b is not None + + def set_explicit(self, is_explicit): + self.is_explicit = is_explicit + + def has_explicit_builder(self): + """Return whether this Node has an explicit builder + + This allows an internal Builder created by SCons to be marked + non-explicit, so that it can be overridden by an explicit + builder that the user supplies (the canonical example being + directories).""" + try: + return self.is_explicit + except AttributeError: + self.is_explicit = None + return self.is_explicit + + def get_builder(self, default_builder=None): + """Return the set builder, or a specified default value""" + try: + return self.builder + except AttributeError: + return default_builder + + multiple_side_effect_has_builder = has_builder + + def is_derived(self): + """ + Returns true iff this node is derived (i.e. built). + + This should return true only for nodes whose path should be in + the variant directory when duplicate=0 and should contribute their build + signatures when they are used as source files to other derived files. For + example: source with source builders are not derived in this sense, + and hence should not return true. + """ + return self.has_builder() or self.side_effect + + def alter_targets(self): + """Return a list of alternate targets for this Node. + """ + return [], None + + def get_found_includes(self, env, scanner, path): + """Return the scanned include lines (implicit dependencies) + found in this node. + + The default is no implicit dependencies. We expect this method + to be overridden by any subclass that can be scanned for + implicit dependencies. + """ + return [] + + def get_implicit_deps(self, env, scanner, path): + """Return a list of implicit dependencies for this node. + + This method exists to handle recursive invocation of the scanner + on the implicit dependencies returned by the scanner, if the + scanner's recursive flag says that we should. + """ + if not scanner: + return [] + + # Give the scanner a chance to select a more specific scanner + # for this Node. + #scanner = scanner.select(self) + + nodes = [self] + seen = {} + seen[self] = 1 + deps = [] + while nodes: + n = nodes.pop(0) + d = [x for x in n.get_found_includes(env, scanner, path) if x not in seen] + if d: + deps.extend(d) + for n in d: + seen[n] = 1 + nodes.extend(scanner.recurse_nodes(d)) + + return deps + + def get_env_scanner(self, env, kw={}): + return env.get_scanner(self.scanner_key()) + + def get_target_scanner(self): + return self.builder.target_scanner + + def get_source_scanner(self, node): + """Fetch the source scanner for the specified node + + NOTE: "self" is the target being built, "node" is + the source file for which we want to fetch the scanner. + + Implies self.has_builder() is true; again, expect to only be + called from locations where this is already verified. + + This function may be called very often; it attempts to cache + the scanner found to improve performance. + """ + scanner = None + try: + scanner = self.builder.source_scanner + except AttributeError: + pass + if not scanner: + # The builder didn't have an explicit scanner, so go look up + # a scanner from env['SCANNERS'] based on the node's scanner + # key (usually the file extension). + scanner = self.get_env_scanner(self.get_build_env()) + if scanner: + scanner = scanner.select(node) + return scanner + + def add_to_implicit(self, deps): + if not hasattr(self, 'implicit') or self.implicit is None: + self.implicit = [] + self.implicit_set = set() + self._children_reset() + self._add_child(self.implicit, self.implicit_set, deps) + + def scan(self): + """Scan this node's dependents for implicit dependencies.""" + # Don't bother scanning non-derived files, because we don't + # care what their dependencies are. + # Don't scan again, if we already have scanned. + if self.implicit is not None: + return + self.implicit = [] + self.implicit_set = set() + self._children_reset() + if not self.has_builder(): + return + + build_env = self.get_build_env() + executor = self.get_executor() + + # Here's where we implement --implicit-cache. + if implicit_cache and not implicit_deps_changed: + implicit = self.get_stored_implicit() + if implicit is not None: + # We now add the implicit dependencies returned from the + # stored .sconsign entry to have already been converted + # to Nodes for us. (We used to run them through a + # source_factory function here.) + + # Update all of the targets with them. This + # essentially short-circuits an N*M scan of the + # sources for each individual target, which is a hell + # of a lot more efficient. + for tgt in executor.get_all_targets(): + tgt.add_to_implicit(implicit) + + if implicit_deps_unchanged or self.is_up_to_date(): + return + # one of this node's sources has changed, + # so we must recalculate the implicit deps for all targets + for tgt in executor.get_all_targets(): + tgt.implicit = [] + tgt.implicit_set = set() + + # Have the executor scan the sources. + executor.scan_sources(self.builder.source_scanner) + + # If there's a target scanner, have the executor scan the target + # node itself and associated targets that might be built. + scanner = self.get_target_scanner() + if scanner: + executor.scan_targets(scanner) + + def scanner_key(self): + return None + + def select_scanner(self, scanner): + """Selects a scanner for this Node. + + This is a separate method so it can be overridden by Node + subclasses (specifically, Node.FS.Dir) that *must* use their + own Scanner and don't select one the Scanner.Selector that's + configured for the target. + """ + return scanner.select(self) + + def env_set(self, env, safe=0): + if safe and self.env: + return + self.env = env + + # + # SIGNATURE SUBSYSTEM + # + + NodeInfo = NodeInfoBase + BuildInfo = BuildInfoBase + + def new_ninfo(self): + ninfo = self.NodeInfo(self) + return ninfo + + def get_ninfo(self): + try: + return self.ninfo + except AttributeError: + self.ninfo = self.new_ninfo() + return self.ninfo + + def new_binfo(self): + binfo = self.BuildInfo(self) + return binfo + + def get_binfo(self): + """ + Fetch a node's build information. + + node - the node whose sources will be collected + cache - alternate node to use for the signature cache + returns - the build signature + + This no longer handles the recursive descent of the + node's children's signatures. We expect that they're + already built and updated by someone else, if that's + what's wanted. + """ + try: + return self.binfo + except AttributeError: + pass + + binfo = self.new_binfo() + self.binfo = binfo + + executor = self.get_executor() + ignore_set = self.ignore_set + + if self.has_builder(): + binfo.bact = str(executor) + binfo.bactsig = SCons.Util.MD5signature(executor.get_contents()) + + if self._specific_sources: + sources = [] + for s in self.sources: + if s not in ignore_set: + sources.append(s) + else: + sources = executor.get_unignored_sources(self, self.ignore) + seen = set() + bsources = [] + bsourcesigs = [] + for s in sources: + if not s in seen: + seen.add(s) + bsources.append(s) + bsourcesigs.append(s.get_ninfo()) + binfo.bsources = bsources + binfo.bsourcesigs = bsourcesigs + + depends = self.depends + dependsigs = [] + for d in depends: + if d not in ignore_set: + dependsigs.append(d.get_ninfo()) + binfo.bdepends = depends + binfo.bdependsigs = dependsigs + + implicit = self.implicit or [] + implicitsigs = [] + for i in implicit: + if i not in ignore_set: + implicitsigs.append(i.get_ninfo()) + binfo.bimplicit = implicit + binfo.bimplicitsigs = implicitsigs + + return binfo + + def del_binfo(self): + """Delete the build info from this node.""" + try: + delattr(self, 'binfo') + except AttributeError: + pass + + def get_csig(self): + try: + return self.ninfo.csig + except AttributeError: + ninfo = self.get_ninfo() + ninfo.csig = SCons.Util.MD5signature(self.get_contents()) + return self.ninfo.csig + + def get_cachedir_csig(self): + return self.get_csig() + + def store_info(self): + """Make the build signature permanent (that is, store it in the + .sconsign file or equivalent).""" + pass + + def do_not_store_info(self): + pass + + def get_stored_info(self): + return None + + def get_stored_implicit(self): + """Fetch the stored implicit dependencies""" + return None + + # + # + # + + def set_precious(self, precious = 1): + """Set the Node's precious value.""" + self.precious = precious + + def set_noclean(self, noclean = 1): + """Set the Node's noclean value.""" + # Make sure noclean is an integer so the --debug=stree + # output in Util.py can use it as an index. + self.noclean = noclean and 1 or 0 + + def set_nocache(self, nocache = 1): + """Set the Node's nocache value.""" + # Make sure nocache is an integer so the --debug=stree + # output in Util.py can use it as an index. + self.nocache = nocache and 1 or 0 + + def set_always_build(self, always_build = 1): + """Set the Node's always_build value.""" + self.always_build = always_build + + def exists(self): + """Does this node exists?""" + # All node exist by default: + return 1 + + def rexists(self): + """Does this node exist locally or in a repositiory?""" + # There are no repositories by default: + return self.exists() + + def missing(self): + return not self.is_derived() and \ + not self.linked and \ + not self.rexists() + + def remove(self): + """Remove this Node: no-op by default.""" + return None + + def add_dependency(self, depend): + """Adds dependencies.""" + try: + self._add_child(self.depends, self.depends_set, depend) + except TypeError, e: + e = e.args[0] + if SCons.Util.is_List(e): + s = list(map(str, e)) + else: + s = str(e) + raise SCons.Errors.UserError("attempted to add a non-Node dependency to %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e))) + + def add_prerequisite(self, prerequisite): + """Adds prerequisites""" + self.prerequisites.extend(prerequisite) + self._children_reset() + + def add_ignore(self, depend): + """Adds dependencies to ignore.""" + try: + self._add_child(self.ignore, self.ignore_set, depend) + except TypeError, e: + e = e.args[0] + if SCons.Util.is_List(e): + s = list(map(str, e)) + else: + s = str(e) + raise SCons.Errors.UserError("attempted to ignore a non-Node dependency of %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e))) + + def add_source(self, source): + """Adds sources.""" + if self._specific_sources: + return + try: + self._add_child(self.sources, self.sources_set, source) + except TypeError, e: + e = e.args[0] + if SCons.Util.is_List(e): + s = list(map(str, e)) + else: + s = str(e) + raise SCons.Errors.UserError("attempted to add a non-Node as source of %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e))) + + def _add_child(self, collection, set, child): + """Adds 'child' to 'collection', first checking 'set' to see if it's + already present.""" + #if type(child) is not type([]): + # child = [child] + #for c in child: + # if not isinstance(c, Node): + # raise TypeError, c + added = None + for c in child: + if c not in set: + set.add(c) + collection.append(c) + added = 1 + if added: + self._children_reset() + + def set_specific_source(self, source): + self.add_source(source) + self._specific_sources = True + + def add_wkid(self, wkid): + """Add a node to the list of kids waiting to be evaluated""" + if self.wkids is not None: + self.wkids.append(wkid) + + def _children_reset(self): + self.clear_memoized_values() + # We need to let the Executor clear out any calculated + # build info that it's cached so we can re-calculate it. + self.executor_cleanup() + + memoizer_counters.append(SCons.Memoize.CountValue('_children_get')) + + def _children_get(self): + try: + return self._memo['children_get'] + except KeyError: + pass + + # The return list may contain duplicate Nodes, especially in + # source trees where there are a lot of repeated #includes + # of a tangle of .h files. Profiling shows, however, that + # eliminating the duplicates with a brute-force approach that + # preserves the order (that is, something like: + # + # u = [] + # for n in list: + # if n not in u: + # u.append(n)" + # + # takes more cycles than just letting the underlying methods + # hand back cached values if a Node's information is requested + # multiple times. (Other methods of removing duplicates, like + # using dictionary keys, lose the order, and the only ordered + # dictionary patterns I found all ended up using "not in" + # internally anyway...) + if self.ignore_set: + if self.implicit is None: + iter = chain(self.sources,self.depends) + else: + iter = chain(self.sources, self.depends, self.implicit) + + children = [] + for i in iter: + if i not in self.ignore_set: + children.append(i) + else: + if self.implicit is None: + children = self.sources + self.depends + else: + children = self.sources + self.depends + self.implicit + + self._memo['children_get'] = children + return children + + def all_children(self, scan=1): + """Return a list of all the node's direct children.""" + if scan: + self.scan() + + # The return list may contain duplicate Nodes, especially in + # source trees where there are a lot of repeated #includes + # of a tangle of .h files. Profiling shows, however, that + # eliminating the duplicates with a brute-force approach that + # preserves the order (that is, something like: + # + # u = [] + # for n in list: + # if n not in u: + # u.append(n)" + # + # takes more cycles than just letting the underlying methods + # hand back cached values if a Node's information is requested + # multiple times. (Other methods of removing duplicates, like + # using dictionary keys, lose the order, and the only ordered + # dictionary patterns I found all ended up using "not in" + # internally anyway...) + if self.implicit is None: + return self.sources + self.depends + else: + return self.sources + self.depends + self.implicit + + def children(self, scan=1): + """Return a list of the node's direct children, minus those + that are ignored by this node.""" + if scan: + self.scan() + return self._children_get() + + def set_state(self, state): + self.state = state + + def get_state(self): + return self.state + + def state_has_changed(self, target, prev_ni): + return (self.state != SCons.Node.up_to_date) + + def get_env(self): + env = self.env + if not env: + import SCons.Defaults + env = SCons.Defaults.DefaultEnvironment() + return env + + def changed_since_last_build(self, target, prev_ni): + """ + + Must be overridden in a specific subclass to return True if this + Node (a dependency) has changed since the last time it was used + to build the specified target. prev_ni is this Node's state (for + example, its file timestamp, length, maybe content signature) + as of the last time the target was built. + + Note that this method is called through the dependency, not the + target, because a dependency Node must be able to use its own + logic to decide if it changed. For example, File Nodes need to + obey if we're configured to use timestamps, but Python Value Nodes + never use timestamps and always use the content. If this method + were called through the target, then each Node's implementation + of this method would have to have more complicated logic to + handle all the different Node types on which it might depend. + """ + raise NotImplementedError + + def Decider(self, function): + SCons.Util.AddMethod(self, function, 'changed_since_last_build') + + def changed(self, node=None): + """ + Returns if the node is up-to-date with respect to the BuildInfo + stored last time it was built. The default behavior is to compare + it against our own previously stored BuildInfo, but the stored + BuildInfo from another Node (typically one in a Repository) + can be used instead. + + Note that we now *always* check every dependency. We used to + short-circuit the check by returning as soon as we detected + any difference, but we now rely on checking every dependency + to make sure that any necessary Node information (for example, + the content signature of an #included .h file) is updated. + """ + t = 0 + if t: Trace('changed(%s [%s], %s)' % (self, classname(self), node)) + if node is None: + node = self + + result = False + + bi = node.get_stored_info().binfo + then = bi.bsourcesigs + bi.bdependsigs + bi.bimplicitsigs + children = self.children() + + diff = len(children) - len(then) + if diff: + # The old and new dependency lists are different lengths. + # This always indicates that the Node must be rebuilt. + # We also extend the old dependency list with enough None + # entries to equal the new dependency list, for the benefit + # of the loop below that updates node information. + then.extend([None] * diff) + if t: Trace(': old %s new %s' % (len(then), len(children))) + result = True + + for child, prev_ni in zip(children, then): + if child.changed_since_last_build(self, prev_ni): + if t: Trace(': %s changed' % child) + result = True + + contents = self.get_executor().get_contents() + if self.has_builder(): + import SCons.Util + newsig = SCons.Util.MD5signature(contents) + if bi.bactsig != newsig: + if t: Trace(': bactsig %s != newsig %s' % (bi.bactsig, newsig)) + result = True + + if not result: + if t: Trace(': up to date') + + if t: Trace('\n') + + return result + + def is_up_to_date(self): + """Default check for whether the Node is current: unknown Node + subtypes are always out of date, so they will always get built.""" + return None + + def children_are_up_to_date(self): + """Alternate check for whether the Node is current: If all of + our children were up-to-date, then this Node was up-to-date, too. + + The SCons.Node.Alias and SCons.Node.Python.Value subclasses + rebind their current() method to this method.""" + # Allow the children to calculate their signatures. + self.binfo = self.get_binfo() + if self.always_build: + return None + state = 0 + for kid in self.children(None): + s = kid.get_state() + if s and (not state or s > state): + state = s + return (state == 0 or state == SCons.Node.up_to_date) + + def is_literal(self): + """Always pass the string representation of a Node to + the command interpreter literally.""" + return 1 + + def render_include_tree(self): + """ + Return a text representation, suitable for displaying to the + user, of the include tree for the sources of this node. + """ + if self.is_derived() and self.env: + env = self.get_build_env() + for s in self.sources: + scanner = self.get_source_scanner(s) + if scanner: + path = self.get_build_scanner_path(scanner) + else: + path = None + def f(node, env=env, scanner=scanner, path=path): + return node.get_found_includes(env, scanner, path) + return SCons.Util.render_tree(s, f, 1) + else: + return None + + def get_abspath(self): + """ + Return an absolute path to the Node. This will return simply + str(Node) by default, but for Node types that have a concept of + relative path, this might return something different. + """ + return str(self) + + def for_signature(self): + """ + Return a string representation of the Node that will always + be the same for this particular Node, no matter what. This + is by contrast to the __str__() method, which might, for + instance, return a relative path for a file Node. The purpose + of this method is to generate a value to be used in signature + calculation for the command line used to build a target, and + we use this method instead of str() to avoid unnecessary + rebuilds. This method does not need to return something that + would actually work in a command line; it can return any kind of + nonsense, so long as it does not change. + """ + return str(self) + + def get_string(self, for_signature): + """This is a convenience function designed primarily to be + used in command generators (i.e., CommandGeneratorActions or + Environment variables that are callable), which are called + with a for_signature argument that is nonzero if the command + generator is being called to generate a signature for the + command line, which determines if we should rebuild or not. + + Such command generators should use this method in preference + to str(Node) when converting a Node to a string, passing + in the for_signature parameter, such that we will call + Node.for_signature() or str(Node) properly, depending on whether + we are calculating a signature or actually constructing a + command line.""" + if for_signature: + return self.for_signature() + return str(self) + + def get_subst_proxy(self): + """ + This method is expected to return an object that will function + exactly like this Node, except that it implements any additional + special features that we would like to be in effect for + Environment variable substitution. The principle use is that + some Nodes would like to implement a __getattr__() method, + but putting that in the Node type itself has a tendency to kill + performance. We instead put it in a proxy and return it from + this method. It is legal for this method to return self + if no new functionality is needed for Environment substitution. + """ + return self + + def explain(self): + if not self.exists(): + return "building `%s' because it doesn't exist\n" % self + + if self.always_build: + return "rebuilding `%s' because AlwaysBuild() is specified\n" % self + + old = self.get_stored_info() + if old is None: + return None + + old = old.binfo + old.prepare_dependencies() + + try: + old_bkids = old.bsources + old.bdepends + old.bimplicit + old_bkidsigs = old.bsourcesigs + old.bdependsigs + old.bimplicitsigs + except AttributeError: + return "Cannot explain why `%s' is being rebuilt: No previous build information found\n" % self + + new = self.get_binfo() + + new_bkids = new.bsources + new.bdepends + new.bimplicit + new_bkidsigs = new.bsourcesigs + new.bdependsigs + new.bimplicitsigs + + osig = dict(zip(old_bkids, old_bkidsigs)) + nsig = dict(zip(new_bkids, new_bkidsigs)) + + # The sources and dependencies we'll want to report are all stored + # as relative paths to this target's directory, but we want to + # report them relative to the top-level SConstruct directory, + # so we only print them after running them through this lambda + # to turn them into the right relative Node and then return + # its string. + def stringify( s, E=self.dir.Entry ) : + if hasattr( s, 'dir' ) : + return str(E(s)) + return str(s) + + lines = [] + + removed = [x for x in old_bkids if not x in new_bkids] + if removed: + removed = list(map(stringify, removed)) + fmt = "`%s' is no longer a dependency\n" + lines.extend([fmt % s for s in removed]) + + for k in new_bkids: + if not k in old_bkids: + lines.append("`%s' is a new dependency\n" % stringify(k)) + elif k.changed_since_last_build(self, osig[k]): + lines.append("`%s' changed\n" % stringify(k)) + + if len(lines) == 0 and old_bkids != new_bkids: + lines.append("the dependency order changed:\n" + + "%sold: %s\n" % (' '*15, list(map(stringify, old_bkids))) + + "%snew: %s\n" % (' '*15, list(map(stringify, new_bkids)))) + + if len(lines) == 0: + def fmt_with_title(title, strlines): + lines = strlines.split('\n') + sep = '\n' + ' '*(15 + len(title)) + return ' '*15 + title + sep.join(lines) + '\n' + if old.bactsig != new.bactsig: + if old.bact == new.bact: + lines.append("the contents of the build action changed\n" + + fmt_with_title('action: ', new.bact)) + else: + lines.append("the build action changed:\n" + + fmt_with_title('old: ', old.bact) + + fmt_with_title('new: ', new.bact)) + + if len(lines) == 0: + return "rebuilding `%s' for unknown reasons\n" % self + + preamble = "rebuilding `%s' because" % self + if len(lines) == 1: + return "%s %s" % (preamble, lines[0]) + else: + lines = ["%s:\n" % preamble] + lines + return ( ' '*11).join(lines) + +class NodeList(collections.UserList): + def __str__(self): + return str(list(map(str, self.data))) + +def get_children(node, parent): return node.children() +def ignore_cycle(node, stack): pass +def do_nothing(node, parent): pass + +class Walker(object): + """An iterator for walking a Node tree. + + This is depth-first, children are visited before the parent. + The Walker object can be initialized with any node, and + returns the next node on the descent with each get_next() call. + 'kids_func' is an optional function that will be called to + get the children of a node instead of calling 'children'. + 'cycle_func' is an optional function that will be called + when a cycle is detected. + + This class does not get caught in node cycles caused, for example, + by C header file include loops. + """ + def __init__(self, node, kids_func=get_children, + cycle_func=ignore_cycle, + eval_func=do_nothing): + self.kids_func = kids_func + self.cycle_func = cycle_func + self.eval_func = eval_func + node.wkids = copy.copy(kids_func(node, None)) + self.stack = [node] + self.history = {} # used to efficiently detect and avoid cycles + self.history[node] = None + + def get_next(self): + """Return the next node for this walk of the tree. + + This function is intentionally iterative, not recursive, + to sidestep any issues of stack size limitations. + """ + + while self.stack: + if self.stack[-1].wkids: + node = self.stack[-1].wkids.pop(0) + if not self.stack[-1].wkids: + self.stack[-1].wkids = None + if node in self.history: + self.cycle_func(node, self.stack) + else: + node.wkids = copy.copy(self.kids_func(node, self.stack[-1])) + self.stack.append(node) + self.history[node] = None + else: + node = self.stack.pop() + del self.history[node] + if node: + if self.stack: + parent = self.stack[-1] + else: + parent = None + self.eval_func(node, parent) + return node + return None + + def is_done(self): + return not self.stack + + +arg2nodes_lookups = [] + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Options/BoolOption.py b/scons/scons-local-2.3.0/SCons/Options/BoolOption.py new file mode 100644 index 000000000..f1569a552 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Options/BoolOption.py @@ -0,0 +1,50 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Options/BoolOption.py 2013/03/03 09:48:35 garyo" + +__doc__ = """Place-holder for the old SCons.Options module hierarchy + +This is for backwards compatibility. The new equivalent is the Variables/ +class hierarchy. These will have deprecation warnings added (some day), +and will then be removed entirely (some day). +""" + +import SCons.Variables +import SCons.Warnings + +warned = False + +def BoolOption(*args, **kw): + global warned + if not warned: + msg = "The BoolOption() function is deprecated; use the BoolVariable() function instead." + SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) + warned = True + return SCons.Variables.BoolVariable(*args, **kw) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Options/EnumOption.py b/scons/scons-local-2.3.0/SCons/Options/EnumOption.py new file mode 100644 index 000000000..9207e718a --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Options/EnumOption.py @@ -0,0 +1,50 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Options/EnumOption.py 2013/03/03 09:48:35 garyo" + +__doc__ = """Place-holder for the old SCons.Options module hierarchy + +This is for backwards compatibility. The new equivalent is the Variables/ +class hierarchy. These will have deprecation warnings added (some day), +and will then be removed entirely (some day). +""" + +import SCons.Variables +import SCons.Warnings + +warned = False + +def EnumOption(*args, **kw): + global warned + if not warned: + msg = "The EnumOption() function is deprecated; use the EnumVariable() function instead." + SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) + warned = True + return SCons.Variables.EnumVariable(*args, **kw) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Options/ListOption.py b/scons/scons-local-2.3.0/SCons/Options/ListOption.py new file mode 100644 index 000000000..cbf0bd98c --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Options/ListOption.py @@ -0,0 +1,50 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Options/ListOption.py 2013/03/03 09:48:35 garyo" + +__doc__ = """Place-holder for the old SCons.Options module hierarchy + +This is for backwards compatibility. The new equivalent is the Variables/ +class hierarchy. These will have deprecation warnings added (some day), +and will then be removed entirely (some day). +""" + +import SCons.Variables +import SCons.Warnings + +warned = False + +def ListOption(*args, **kw): + global warned + if not warned: + msg = "The ListOption() function is deprecated; use the ListVariable() function instead." + SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) + warned = True + return SCons.Variables.ListVariable(*args, **kw) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Options/PackageOption.py b/scons/scons-local-2.3.0/SCons/Options/PackageOption.py new file mode 100644 index 000000000..2b27931d5 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Options/PackageOption.py @@ -0,0 +1,50 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Options/PackageOption.py 2013/03/03 09:48:35 garyo" + +__doc__ = """Place-holder for the old SCons.Options module hierarchy + +This is for backwards compatibility. The new equivalent is the Variables/ +class hierarchy. These will have deprecation warnings added (some day), +and will then be removed entirely (some day). +""" + +import SCons.Variables +import SCons.Warnings + +warned = False + +def PackageOption(*args, **kw): + global warned + if not warned: + msg = "The PackageOption() function is deprecated; use the PackageVariable() function instead." + SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) + warned = True + return SCons.Variables.PackageVariable(*args, **kw) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Options/PathOption.py b/scons/scons-local-2.3.0/SCons/Options/PathOption.py new file mode 100644 index 000000000..56475adb2 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Options/PathOption.py @@ -0,0 +1,76 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Options/PathOption.py 2013/03/03 09:48:35 garyo" + +__doc__ = """Place-holder for the old SCons.Options module hierarchy + +This is for backwards compatibility. The new equivalent is the Variables/ +class hierarchy. These will have deprecation warnings added (some day), +and will then be removed entirely (some day). +""" + +import SCons.Variables +import SCons.Warnings + +warned = False + +class _PathOptionClass(object): + def warn(self): + global warned + if not warned: + msg = "The PathOption() function is deprecated; use the PathVariable() function instead." + SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) + warned = True + + def __call__(self, *args, **kw): + self.warn() + return SCons.Variables.PathVariable(*args, **kw) + + def PathAccept(self, *args, **kw): + self.warn() + return SCons.Variables.PathVariable.PathAccept(*args, **kw) + + def PathIsDir(self, *args, **kw): + self.warn() + return SCons.Variables.PathVariable.PathIsDir(*args, **kw) + + def PathIsDirCreate(self, *args, **kw): + self.warn() + return SCons.Variables.PathVariable.PathIsDirCreate(*args, **kw) + + def PathIsFile(self, *args, **kw): + self.warn() + return SCons.Variables.PathVariable.PathIsFile(*args, **kw) + + def PathExists(self, *args, **kw): + self.warn() + return SCons.Variables.PathVariable.PathExists(*args, **kw) + +PathOption = _PathOptionClass() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Options/__init__.py b/scons/scons-local-2.3.0/SCons/Options/__init__.py new file mode 100644 index 000000000..0004b8f8f --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Options/__init__.py @@ -0,0 +1,67 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Options/__init__.py 2013/03/03 09:48:35 garyo" + +__doc__ = """Place-holder for the old SCons.Options module hierarchy + +This is for backwards compatibility. The new equivalent is the Variables/ +class hierarchy. These will have deprecation warnings added (some day), +and will then be removed entirely (some day). +""" + +import SCons.Variables +import SCons.Warnings + +from BoolOption import BoolOption # okay +from EnumOption import EnumOption # okay +from ListOption import ListOption # naja +from PackageOption import PackageOption # naja +from PathOption import PathOption # okay + +warned = False + +class Options(SCons.Variables.Variables): + def __init__(self, *args, **kw): + global warned + if not warned: + msg = "The Options class is deprecated; use the Variables class instead." + SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg) + warned = True + SCons.Variables.Variables.__init__(self, *args, **kw) + + def AddOptions(self, *args, **kw): + return SCons.Variables.Variables.AddVariables(self, *args, **kw) + + def UnknownOptions(self, *args, **kw): + return SCons.Variables.Variables.UnknownVariables(self, *args, **kw) + + def FormatOptionHelpText(self, *args, **kw): + return SCons.Variables.Variables.FormatVariableHelpText(self, *args, + **kw) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/PathList.py b/scons/scons-local-2.3.0/SCons/PathList.py new file mode 100644 index 000000000..1129d1ed1 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/PathList.py @@ -0,0 +1,233 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/PathList.py 2013/03/03 09:48:35 garyo" + +__doc__ = """SCons.PathList + +A module for handling lists of directory paths (the sort of things +that get set as CPPPATH, LIBPATH, etc.) with as much caching of data and +efficiency as we can while still keeping the evaluation delayed so that we +Do the Right Thing (almost) regardless of how the variable is specified. + +""" + +import os + +import SCons.Memoize +import SCons.Node +import SCons.Util + +# +# Variables to specify the different types of entries in a PathList object: +# + +TYPE_STRING_NO_SUBST = 0 # string with no '$' +TYPE_STRING_SUBST = 1 # string containing '$' +TYPE_OBJECT = 2 # other object + +def node_conv(obj): + """ + This is the "string conversion" routine that we have our substitutions + use to return Nodes, not strings. This relies on the fact that an + EntryProxy object has a get() method that returns the underlying + Node that it wraps, which is a bit of architectural dependence + that we might need to break or modify in the future in response to + additional requirements. + """ + try: + get = obj.get + except AttributeError: + if isinstance(obj, SCons.Node.Node) or SCons.Util.is_Sequence( obj ): + result = obj + else: + result = str(obj) + else: + result = get() + return result + +class _PathList(object): + """ + An actual PathList object. + """ + def __init__(self, pathlist): + """ + Initializes a PathList object, canonicalizing the input and + pre-processing it for quicker substitution later. + + The stored representation of the PathList is a list of tuples + containing (type, value), where the "type" is one of the TYPE_* + variables defined above. We distinguish between: + + strings that contain no '$' and therefore need no + delayed-evaluation string substitution (we expect that there + will be many of these and that we therefore get a pretty + big win from avoiding string substitution) + + strings that contain '$' and therefore need substitution + (the hard case is things like '${TARGET.dir}/include', + which require re-evaluation for every target + source) + + other objects (which may be something like an EntryProxy + that needs a method called to return a Node) + + Pre-identifying the type of each element in the PathList up-front + and storing the type in the list of tuples is intended to reduce + the amount of calculation when we actually do the substitution + over and over for each target. + """ + if SCons.Util.is_String(pathlist): + pathlist = pathlist.split(os.pathsep) + elif not SCons.Util.is_Sequence(pathlist): + pathlist = [pathlist] + + pl = [] + for p in pathlist: + try: + index = p.find('$') + except (AttributeError, TypeError): + type = TYPE_OBJECT + else: + if index == -1: + type = TYPE_STRING_NO_SUBST + else: + type = TYPE_STRING_SUBST + pl.append((type, p)) + + self.pathlist = tuple(pl) + + def __len__(self): return len(self.pathlist) + + def __getitem__(self, i): return self.pathlist[i] + + def subst_path(self, env, target, source): + """ + Performs construction variable substitution on a pre-digested + PathList for a specific target and source. + """ + result = [] + for type, value in self.pathlist: + if type == TYPE_STRING_SUBST: + value = env.subst(value, target=target, source=source, + conv=node_conv) + if SCons.Util.is_Sequence(value): + result.extend(SCons.Util.flatten(value)) + elif value: + result.append(value) + elif type == TYPE_OBJECT: + value = node_conv(value) + if value: + result.append(value) + elif value: + result.append(value) + return tuple(result) + + +class PathListCache(object): + """ + A class to handle caching of PathList lookups. + + This class gets instantiated once and then deleted from the namespace, + so it's used as a Singleton (although we don't enforce that in the + usual Pythonic ways). We could have just made the cache a dictionary + in the module namespace, but putting it in this class allows us to + use the same Memoizer pattern that we use elsewhere to count cache + hits and misses, which is very valuable. + + Lookup keys in the cache are computed by the _PathList_key() method. + Cache lookup should be quick, so we don't spend cycles canonicalizing + all forms of the same lookup key. For example, 'x:y' and ['x', + 'y'] logically represent the same list, but we don't bother to + split string representations and treat those two equivalently. + (Note, however, that we do, treat lists and tuples the same.) + + The main type of duplication we're trying to catch will come from + looking up the same path list from two different clones of the + same construction environment. That is, given + + env2 = env1.Clone() + + both env1 and env2 will have the same CPPPATH value, and we can + cheaply avoid re-parsing both values of CPPPATH by using the + common value from this cache. + """ + if SCons.Memoize.use_memoizer: + __metaclass__ = SCons.Memoize.Memoized_Metaclass + + memoizer_counters = [] + + def __init__(self): + self._memo = {} + + def _PathList_key(self, pathlist): + """ + Returns the key for memoization of PathLists. + + Note that we want this to be pretty quick, so we don't completely + canonicalize all forms of the same list. For example, + 'dir1:$ROOT/dir2' and ['$ROOT/dir1', 'dir'] may logically + represent the same list if you're executing from $ROOT, but + we're not going to bother splitting strings into path elements, + or massaging strings into Nodes, to identify that equivalence. + We just want to eliminate obvious redundancy from the normal + case of re-using exactly the same cloned value for a path. + """ + if SCons.Util.is_Sequence(pathlist): + pathlist = tuple(SCons.Util.flatten(pathlist)) + return pathlist + + memoizer_counters.append(SCons.Memoize.CountDict('PathList', _PathList_key)) + + def PathList(self, pathlist): + """ + Returns the cached _PathList object for the specified pathlist, + creating and caching a new object as necessary. + """ + pathlist = self._PathList_key(pathlist) + try: + memo_dict = self._memo['PathList'] + except KeyError: + memo_dict = {} + self._memo['PathList'] = memo_dict + else: + try: + return memo_dict[pathlist] + except KeyError: + pass + + result = _PathList(pathlist) + + memo_dict[pathlist] = result + + return result + +PathList = PathListCache().PathList + + +del PathListCache + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Platform/__init__.py b/scons/scons-local-2.3.0/SCons/Platform/__init__.py new file mode 100644 index 000000000..5a1d43741 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Platform/__init__.py @@ -0,0 +1,241 @@ +"""SCons.Platform + +SCons platform selection. + +This looks for modules that define a callable object that can modify a +construction environment as appropriate for a given platform. + +Note that we take a more simplistic view of "platform" than Python does. +We're looking for a single string that determines a set of +tool-independent variables with which to initialize a construction +environment. Consequently, we'll examine both sys.platform and os.name +(and anything else that might come in to play) in order to return some +specification which is unique enough for our purposes. + +Note that because this subsysem just *selects* a callable that can +modify a construction environment, it's possible for people to define +their own "platform specification" in an arbitrary callable function. +No one needs to use or tie in to this subsystem in order to roll +their own platform definition. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Platform/__init__.py 2013/03/03 09:48:35 garyo" + +import SCons.compat + +import imp +import os +import sys +import tempfile + +import SCons.Errors +import SCons.Subst +import SCons.Tool + +def platform_default(): + """Return the platform string for our execution environment. + + The returned value should map to one of the SCons/Platform/*.py + files. Since we're architecture independent, though, we don't + care about the machine architecture. + """ + osname = os.name + if osname == 'java': + osname = os._osType + if osname == 'posix': + if sys.platform == 'cygwin': + return 'cygwin' + elif sys.platform.find('irix') != -1: + return 'irix' + elif sys.platform.find('sunos') != -1: + return 'sunos' + elif sys.platform.find('hp-ux') != -1: + return 'hpux' + elif sys.platform.find('aix') != -1: + return 'aix' + elif sys.platform.find('darwin') != -1: + return 'darwin' + else: + return 'posix' + elif os.name == 'os2': + return 'os2' + else: + return sys.platform + +def platform_module(name = platform_default()): + """Return the imported module for the platform. + + This looks for a module name that matches the specified argument. + If the name is unspecified, we fetch the appropriate default for + our execution environment. + """ + full_name = 'SCons.Platform.' + name + if full_name not in sys.modules: + if os.name == 'java': + eval(full_name) + else: + try: + file, path, desc = imp.find_module(name, + sys.modules['SCons.Platform'].__path__) + try: + mod = imp.load_module(full_name, file, path, desc) + finally: + if file: + file.close() + except ImportError: + try: + import zipimport + importer = zipimport.zipimporter( sys.modules['SCons.Platform'].__path__[0] ) + mod = importer.load_module(full_name) + except ImportError: + raise SCons.Errors.UserError("No platform named '%s'" % name) + setattr(SCons.Platform, name, mod) + return sys.modules[full_name] + +def DefaultToolList(platform, env): + """Select a default tool list for the specified platform. + """ + return SCons.Tool.tool_list(platform, env) + +class PlatformSpec(object): + def __init__(self, name, generate): + self.name = name + self.generate = generate + + def __call__(self, *args, **kw): + return self.generate(*args, **kw) + + def __str__(self): + return self.name + +class TempFileMunge(object): + """A callable class. You can set an Environment variable to this, + then call it with a string argument, then it will perform temporary + file substitution on it. This is used to circumvent the long command + line limitation. + + Example usage: + env["TEMPFILE"] = TempFileMunge + env["LINKCOM"] = "${TEMPFILE('$LINK $TARGET $SOURCES')}" + + By default, the name of the temporary file used begins with a + prefix of '@'. This may be configred for other tool chains by + setting '$TEMPFILEPREFIX'. + + env["TEMPFILEPREFIX"] = '-@' # diab compiler + env["TEMPFILEPREFIX"] = '-via' # arm tool chain + """ + def __init__(self, cmd): + self.cmd = cmd + + def __call__(self, target, source, env, for_signature): + if for_signature: + # If we're being called for signature calculation, it's + # because we're being called by the string expansion in + # Subst.py, which has the logic to strip any $( $) that + # may be in the command line we squirreled away. So we + # just return the raw command line and let the upper + # string substitution layers do their thing. + return self.cmd + + # Now we're actually being called because someone is actually + # going to try to execute the command, so we have to do our + # own expansion. + cmd = env.subst_list(self.cmd, SCons.Subst.SUBST_CMD, target, source)[0] + try: + maxline = int(env.subst('$MAXLINELENGTH')) + except ValueError: + maxline = 2048 + + length = 0 + for c in cmd: + length += len(c) + if length <= maxline: + return self.cmd + + # We do a normpath because mktemp() has what appears to be + # a bug in Windows that will use a forward slash as a path + # delimiter. Windows's link mistakes that for a command line + # switch and barfs. + # + # We use the .lnk suffix for the benefit of the Phar Lap + # linkloc linker, which likes to append an .lnk suffix if + # none is given. + (fd, tmp) = tempfile.mkstemp('.lnk', text=True) + native_tmp = SCons.Util.get_native_path(os.path.normpath(tmp)) + + if env['SHELL'] and env['SHELL'] == 'sh': + # The sh shell will try to escape the backslashes in the + # path, so unescape them. + native_tmp = native_tmp.replace('\\', r'\\\\') + # In Cygwin, we want to use rm to delete the temporary + # file, because del does not exist in the sh shell. + rm = env.Detect('rm') or 'del' + else: + # Don't use 'rm' if the shell is not sh, because rm won't + # work with the Windows shells (cmd.exe or command.com) or + # Windows path names. + rm = 'del' + + prefix = env.subst('$TEMPFILEPREFIX') + if not prefix: + prefix = '@' + + args = list(map(SCons.Subst.quote_spaces, cmd[1:])) + os.write(fd, " ".join(args) + "\n") + os.close(fd) + # XXX Using the SCons.Action.print_actions value directly + # like this is bogus, but expedient. This class should + # really be rewritten as an Action that defines the + # __call__() and strfunction() methods and lets the + # normal action-execution logic handle whether or not to + # print/execute the action. The problem, though, is all + # of that is decided before we execute this method as + # part of expanding the $TEMPFILE construction variable. + # Consequently, refactoring this will have to wait until + # we get more flexible with allowing Actions to exist + # independently and get strung together arbitrarily like + # Ant tasks. In the meantime, it's going to be more + # user-friendly to not let obsession with architectural + # purity get in the way of just being helpful, so we'll + # reach into SCons.Action directly. + if SCons.Action.print_actions: + print("Using tempfile "+native_tmp+" for command line:\n"+ + str(cmd[0]) + " " + " ".join(args)) + return [ cmd[0], prefix + native_tmp + '\n' + rm, native_tmp ] + +def Platform(name = platform_default()): + """Select a canned Platform specification. + """ + module = platform_module(name) + spec = PlatformSpec(name, module.generate) + return spec + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Platform/aix.py b/scons/scons-local-2.3.0/SCons/Platform/aix.py new file mode 100644 index 000000000..70dce56be --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Platform/aix.py @@ -0,0 +1,69 @@ +"""engine.SCons.Platform.aix + +Platform-specific initialization for IBM AIX systems. + +There normally shouldn't be any need to import this module directly. It +will usually be imported through the generic SCons.Platform.Platform() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Platform/aix.py 2013/03/03 09:48:35 garyo" + +import os + +import posix + +def get_xlc(env, xlc=None, xlc_r=None, packages=[]): + # Use the AIX package installer tool lslpp to figure out where a + # given xl* compiler is installed and what version it is. + xlcPath = None + xlcVersion = None + + if xlc is None: + xlc = env.get('CC', 'xlc') + if xlc_r is None: + xlc_r = xlc + '_r' + for package in packages: + cmd = "lslpp -fc " + package + " 2>/dev/null | egrep '" + xlc + "([^-_a-zA-Z0-9].*)?$'" + line = os.popen(cmd).readline() + if line: + v, p = line.split(':')[1:3] + xlcVersion = v.split()[1] + xlcPath = p.split()[0] + xlcPath = xlcPath[:xlcPath.rindex('/')] + break + return (xlcPath, xlc, xlc_r, xlcVersion) + +def generate(env): + posix.generate(env) + #Based on AIX 5.2: ARG_MAX=24576 - 3000 for environment expansion + env['MAXLINELENGTH'] = 21576 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Platform/cygwin.py b/scons/scons-local-2.3.0/SCons/Platform/cygwin.py new file mode 100644 index 000000000..92efdf638 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Platform/cygwin.py @@ -0,0 +1,55 @@ +"""SCons.Platform.cygwin + +Platform-specific initialization for Cygwin systems. + +There normally shouldn't be any need to import this module directly. It +will usually be imported through the generic SCons.Platform.Platform() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Platform/cygwin.py 2013/03/03 09:48:35 garyo" + +import posix +from SCons.Platform import TempFileMunge + +def generate(env): + posix.generate(env) + + env['PROGPREFIX'] = '' + env['PROGSUFFIX'] = '.exe' + env['SHLIBPREFIX'] = '' + env['SHLIBSUFFIX'] = '.dll' + env['LIBPREFIXES'] = [ '$LIBPREFIX', '$SHLIBPREFIX' ] + env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ] + env['TEMPFILE'] = TempFileMunge + env['TEMPFILEPREFIX'] = '@' + env['MAXLINELENGTH'] = 2048 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Platform/darwin.py b/scons/scons-local-2.3.0/SCons/Platform/darwin.py new file mode 100644 index 000000000..f1ba6be93 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Platform/darwin.py @@ -0,0 +1,70 @@ +"""engine.SCons.Platform.darwin + +Platform-specific initialization for Mac OS X systems. + +There normally shouldn't be any need to import this module directly. It +will usually be imported through the generic SCons.Platform.Platform() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Platform/darwin.py 2013/03/03 09:48:35 garyo" + +import posix +import os + +def generate(env): + posix.generate(env) + env['SHLIBSUFFIX'] = '.dylib' + # put macports paths at front to override Apple's versions, fink path is after + # For now let people who want Macports or Fink tools specify it! + # env['ENV']['PATH'] = '/opt/local/bin:/opt/local/sbin:' + env['ENV']['PATH'] + ':/sw/bin' + + # Store extra system paths in env['ENV']['PATHOSX'] + + filelist = ['/etc/paths',] + # make sure this works on Macs with Tiger or earlier + try: + dirlist = os.listdir('/etc/paths.d') + except: + dirlist = [] + + for file in dirlist: + filelist.append('/etc/paths.d/'+file) + + for file in filelist: + if os.path.isfile(file): + f = open(file, 'r') + lines = f.readlines() + for line in lines: + if line: + env.AppendENVPath('PATHOSX', line.strip('\n')) + f.close() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Platform/hpux.py b/scons/scons-local-2.3.0/SCons/Platform/hpux.py new file mode 100644 index 000000000..232baf43a --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Platform/hpux.py @@ -0,0 +1,46 @@ +"""engine.SCons.Platform.hpux + +Platform-specific initialization for HP-UX systems. + +There normally shouldn't be any need to import this module directly. It +will usually be imported through the generic SCons.Platform.Platform() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Platform/hpux.py 2013/03/03 09:48:35 garyo" + +import posix + +def generate(env): + posix.generate(env) + #Based on HP-UX11i: ARG_MAX=2048000 - 3000 for environment expansion + env['MAXLINELENGTH'] = 2045000 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Platform/irix.py b/scons/scons-local-2.3.0/SCons/Platform/irix.py new file mode 100644 index 000000000..f4bc6f8ce --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Platform/irix.py @@ -0,0 +1,44 @@ +"""SCons.Platform.irix + +Platform-specific initialization for SGI IRIX systems. + +There normally shouldn't be any need to import this module directly. It +will usually be imported through the generic SCons.Platform.Platform() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Platform/irix.py 2013/03/03 09:48:35 garyo" + +import posix + +def generate(env): + posix.generate(env) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Platform/os2.py b/scons/scons-local-2.3.0/SCons/Platform/os2.py new file mode 100644 index 000000000..1409711e9 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Platform/os2.py @@ -0,0 +1,58 @@ +"""SCons.Platform.os2 + +Platform-specific initialization for OS/2 systems. + +There normally shouldn't be any need to import this module directly. It +will usually be imported through the generic SCons.Platform.Platform() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Platform/os2.py 2013/03/03 09:48:35 garyo" +import win32 + +def generate(env): + if 'ENV' not in env: + env['ENV'] = {} + env['OBJPREFIX'] = '' + env['OBJSUFFIX'] = '.obj' + env['SHOBJPREFIX'] = '$OBJPREFIX' + env['SHOBJSUFFIX'] = '$OBJSUFFIX' + env['PROGPREFIX'] = '' + env['PROGSUFFIX'] = '.exe' + env['LIBPREFIX'] = '' + env['LIBSUFFIX'] = '.lib' + env['SHLIBPREFIX'] = '' + env['SHLIBSUFFIX'] = '.dll' + env['LIBPREFIXES'] = '$LIBPREFIX' + env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ] + env['HOST_OS'] = 'os2' + env['HOST_ARCH'] = win32.get_architecture().arch + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Platform/posix.py b/scons/scons-local-2.3.0/SCons/Platform/posix.py new file mode 100644 index 000000000..38b2a8def --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Platform/posix.py @@ -0,0 +1,263 @@ +"""SCons.Platform.posix + +Platform-specific initialization for POSIX (Linux, UNIX, etc.) systems. + +There normally shouldn't be any need to import this module directly. It +will usually be imported through the generic SCons.Platform.Platform() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Platform/posix.py 2013/03/03 09:48:35 garyo" + +import errno +import os +import os.path +import subprocess +import sys +import select + +import SCons.Util +from SCons.Platform import TempFileMunge + +exitvalmap = { + 2 : 127, + 13 : 126, +} + +def escape(arg): + "escape shell special characters" + slash = '\\' + special = '"$()' + + arg = arg.replace(slash, slash+slash) + for c in special: + arg = arg.replace(c, slash+c) + + return '"' + arg + '"' + +def exec_system(l, env): + stat = os.system(' '.join(l)) + if stat & 0xff: + return stat | 0x80 + return stat >> 8 + +def exec_spawnvpe(l, env): + stat = os.spawnvpe(os.P_WAIT, l[0], l, env) + # os.spawnvpe() returns the actual exit code, not the encoding + # returned by os.waitpid() or os.system(). + return stat + +def exec_fork(l, env): + pid = os.fork() + if not pid: + # Child process. + exitval = 127 + try: + os.execvpe(l[0], l, env) + except OSError, e: + exitval = exitvalmap.get(e[0], e[0]) + sys.stderr.write("scons: %s: %s\n" % (l[0], e[1])) + os._exit(exitval) + else: + # Parent process. + pid, stat = os.waitpid(pid, 0) + if stat & 0xff: + return stat | 0x80 + return stat >> 8 + +def _get_env_command(sh, escape, cmd, args, env): + s = ' '.join(args) + if env: + l = ['env', '-'] + \ + [escape(t[0])+'='+escape(t[1]) for t in env.items()] + \ + [sh, '-c', escape(s)] + s = ' '.join(l) + return s + +def env_spawn(sh, escape, cmd, args, env): + return exec_system([_get_env_command( sh, escape, cmd, args, env)], env) + +def spawnvpe_spawn(sh, escape, cmd, args, env): + return exec_spawnvpe([sh, '-c', ' '.join(args)], env) + +def fork_spawn(sh, escape, cmd, args, env): + return exec_fork([sh, '-c', ' '.join(args)], env) + +def process_cmd_output(cmd_stdout, cmd_stderr, stdout, stderr): + stdout_eof = stderr_eof = 0 + while not (stdout_eof and stderr_eof): + try: + (i,o,e) = select.select([cmd_stdout, cmd_stderr], [], []) + if cmd_stdout in i: + str = cmd_stdout.read() + if len(str) == 0: + stdout_eof = 1 + elif stdout is not None: + stdout.write(str) + if cmd_stderr in i: + str = cmd_stderr.read() + if len(str) == 0: + #sys.__stderr__.write( "stderr_eof=1\n" ) + stderr_eof = 1 + else: + #sys.__stderr__.write( "str(stderr) = %s\n" % str ) + stderr.write(str) + except select.error, (_errno, _strerror): + if _errno != errno.EINTR: + raise + +def exec_popen3(l, env, stdout, stderr): + proc = subprocess.Popen(' '.join(l), + stdout=stdout, + stderr=stderr, + shell=True) + stat = proc.wait() + if stat & 0xff: + return stat | 0x80 + return stat >> 8 + +def exec_piped_fork(l, env, stdout, stderr): + # spawn using fork / exec and providing a pipe for the command's + # stdout / stderr stream + if stdout != stderr: + (rFdOut, wFdOut) = os.pipe() + (rFdErr, wFdErr) = os.pipe() + else: + (rFdOut, wFdOut) = os.pipe() + rFdErr = rFdOut + wFdErr = wFdOut + # do the fork + pid = os.fork() + if not pid: + # Child process + os.close( rFdOut ) + if rFdOut != rFdErr: + os.close( rFdErr ) + os.dup2( wFdOut, 1 ) # is there some symbolic way to do that ? + os.dup2( wFdErr, 2 ) + os.close( wFdOut ) + if stdout != stderr: + os.close( wFdErr ) + exitval = 127 + try: + os.execvpe(l[0], l, env) + except OSError, e: + exitval = exitvalmap.get(e[0], e[0]) + stderr.write("scons: %s: %s\n" % (l[0], e[1])) + os._exit(exitval) + else: + # Parent process + pid, stat = os.waitpid(pid, 0) + os.close( wFdOut ) + if stdout != stderr: + os.close( wFdErr ) + childOut = os.fdopen( rFdOut ) + if stdout != stderr: + childErr = os.fdopen( rFdErr ) + else: + childErr = childOut + process_cmd_output(childOut, childErr, stdout, stderr) + os.close( rFdOut ) + if stdout != stderr: + os.close( rFdErr ) + if stat & 0xff: + return stat | 0x80 + return stat >> 8 + +def piped_env_spawn(sh, escape, cmd, args, env, stdout, stderr): + # spawn using Popen3 combined with the env command + # the command name and the command's stdout is written to stdout + # the command's stderr is written to stderr + return exec_popen3([_get_env_command(sh, escape, cmd, args, env)], + env, stdout, stderr) + +def piped_fork_spawn(sh, escape, cmd, args, env, stdout, stderr): + # spawn using fork / exec and providing a pipe for the command's + # stdout / stderr stream + return exec_piped_fork([sh, '-c', ' '.join(args)], + env, stdout, stderr) + + + +def generate(env): + # If os.spawnvpe() exists, we use it to spawn commands. Otherwise + # if the env utility exists, we use os.system() to spawn commands, + # finally we fall back on os.fork()/os.exec(). + # + # os.spawnvpe() is prefered because it is the most efficient. But + # for Python versions without it, os.system() is prefered because it + # is claimed that it works better with threads (i.e. -j) and is more + # efficient than forking Python. + # + # NB: Other people on the scons-users mailing list have claimed that + # os.fork()/os.exec() works better than os.system(). There may just + # not be a default that works best for all users. + + if 'spawnvpe' in os.__dict__: + spawn = spawnvpe_spawn + elif env.Detect('env'): + spawn = env_spawn + else: + spawn = fork_spawn + + if env.Detect('env'): + pspawn = piped_env_spawn + else: + pspawn = piped_fork_spawn + + if 'ENV' not in env: + env['ENV'] = {} + env['ENV']['PATH'] = '/usr/local/bin:/opt/bin:/bin:/usr/bin' + env['OBJPREFIX'] = '' + env['OBJSUFFIX'] = '.o' + env['SHOBJPREFIX'] = '$OBJPREFIX' + env['SHOBJSUFFIX'] = '$OBJSUFFIX' + env['PROGPREFIX'] = '' + env['PROGSUFFIX'] = '' + env['LIBPREFIX'] = 'lib' + env['LIBSUFFIX'] = '.a' + env['SHLIBPREFIX'] = '$LIBPREFIX' + env['SHLIBSUFFIX'] = '.so' + env['LIBPREFIXES'] = [ '$LIBPREFIX' ] + env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ] + env['PSPAWN'] = pspawn + env['SPAWN'] = spawn + env['SHELL'] = 'sh' + env['ESCAPE'] = escape + env['TEMPFILE'] = TempFileMunge + env['TEMPFILEPREFIX'] = '@' + #Based on LINUX: ARG_MAX=ARG_MAX=131072 - 3000 for environment expansion + #Note: specific platforms might rise or lower this value + env['MAXLINELENGTH'] = 128072 + + # This platform supports RPATH specifications. + env['__RPATH'] = '$_RPATH' + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Platform/sunos.py b/scons/scons-local-2.3.0/SCons/Platform/sunos.py new file mode 100644 index 000000000..23e876c52 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Platform/sunos.py @@ -0,0 +1,50 @@ +"""engine.SCons.Platform.sunos + +Platform-specific initialization for Sun systems. + +There normally shouldn't be any need to import this module directly. It +will usually be imported through the generic SCons.Platform.Platform() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Platform/sunos.py 2013/03/03 09:48:35 garyo" + +import posix + +def generate(env): + posix.generate(env) + # Based on sunSparc 8:32bit + # ARG_MAX=1048320 - 3000 for environment expansion + env['MAXLINELENGTH'] = 1045320 + env['PKGINFO'] = 'pkginfo' + env['PKGCHK'] = '/usr/sbin/pkgchk' + env['ENV']['PATH'] = env['ENV']['PATH'] + ':/opt/SUNWspro/bin:/usr/ccs/bin' + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Platform/win32.py b/scons/scons-local-2.3.0/SCons/Platform/win32.py new file mode 100644 index 000000000..3def1f834 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Platform/win32.py @@ -0,0 +1,415 @@ +"""SCons.Platform.win32 + +Platform-specific initialization for Win32 systems. + +There normally shouldn't be any need to import this module directly. It +will usually be imported through the generic SCons.Platform.Platform() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Platform/win32.py 2013/03/03 09:48:35 garyo" + +import os +import os.path +import sys +import tempfile + +from SCons.Platform.posix import exitvalmap +from SCons.Platform import TempFileMunge +import SCons.Util + +try: + import msvcrt + import win32api + import win32con + + msvcrt.get_osfhandle + win32api.SetHandleInformation + win32con.HANDLE_FLAG_INHERIT +except ImportError: + parallel_msg = \ + "you do not seem to have the pywin32 extensions installed;\n" + \ + "\tparallel (-j) builds may not work reliably with open Python files." +except AttributeError: + parallel_msg = \ + "your pywin32 extensions do not support file handle operations;\n" + \ + "\tparallel (-j) builds may not work reliably with open Python files." +else: + parallel_msg = None + + import builtins + + _builtin_file = builtins.file + _builtin_open = builtins.open + + class _scons_file(_builtin_file): + def __init__(self, *args, **kw): + _builtin_file.__init__(self, *args, **kw) + win32api.SetHandleInformation(msvcrt.get_osfhandle(self.fileno()), + win32con.HANDLE_FLAG_INHERIT, 0) + + def _scons_open(*args, **kw): + fp = _builtin_open(*args, **kw) + win32api.SetHandleInformation(msvcrt.get_osfhandle(fp.fileno()), + win32con.HANDLE_FLAG_INHERIT, + 0) + return fp + + builtins.file = _scons_file + builtins.open = _scons_open + +try: + import threading + spawn_lock = threading.Lock() + + # This locked version of spawnve works around a Windows + # MSVCRT bug, because its spawnve is not thread-safe. + # Without this, python can randomly crash while using -jN. + # See the python bug at http://bugs.python.org/issue6476 + # and SCons issue at + # http://scons.tigris.org/issues/show_bug.cgi?id=2449 + def spawnve(mode, file, args, env): + spawn_lock.acquire() + try: + if mode == os.P_WAIT: + ret = os.spawnve(os.P_NOWAIT, file, args, env) + else: + ret = os.spawnve(mode, file, args, env) + finally: + spawn_lock.release() + if mode == os.P_WAIT: + pid, status = os.waitpid(ret, 0) + ret = status >> 8 + return ret +except ImportError: + # Use the unsafe method of spawnve. + # Please, don't try to optimize this try-except block + # away by assuming that the threading module is always present. + # In the test test/option-j.py we intentionally call SCons with + # a fake threading.py that raises an import exception right away, + # simulating a non-existent package. + def spawnve(mode, file, args, env): + return os.spawnve(mode, file, args, env) + +# The upshot of all this is that, if you are using Python 1.5.2, +# you had better have cmd or command.com in your PATH when you run +# scons. + +def piped_spawn(sh, escape, cmd, args, env, stdout, stderr): + # There is no direct way to do that in python. What we do + # here should work for most cases: + # In case stdout (stderr) is not redirected to a file, + # we redirect it into a temporary file tmpFileStdout + # (tmpFileStderr) and copy the contents of this file + # to stdout (stderr) given in the argument + if not sh: + sys.stderr.write("scons: Could not find command interpreter, is it in your PATH?\n") + return 127 + else: + # one temporary file for stdout and stderr + tmpFileStdout = os.path.normpath(tempfile.mktemp()) + tmpFileStderr = os.path.normpath(tempfile.mktemp()) + + # check if output is redirected + stdoutRedirected = 0 + stderrRedirected = 0 + for arg in args: + # are there more possibilities to redirect stdout ? + if (arg.find( ">", 0, 1 ) != -1 or + arg.find( "1>", 0, 2 ) != -1): + stdoutRedirected = 1 + # are there more possibilities to redirect stderr ? + if arg.find( "2>", 0, 2 ) != -1: + stderrRedirected = 1 + + # redirect output of non-redirected streams to our tempfiles + if stdoutRedirected == 0: + args.append(">" + str(tmpFileStdout)) + if stderrRedirected == 0: + args.append("2>" + str(tmpFileStderr)) + + # actually do the spawn + try: + args = [sh, '/C', escape(' '.join(args)) ] + ret = spawnve(os.P_WAIT, sh, args, env) + except OSError, e: + # catch any error + try: + ret = exitvalmap[e[0]] + except KeyError: + sys.stderr.write("scons: unknown OSError exception code %d - %s: %s\n" % (e[0], cmd, e[1])) + if stderr is not None: + stderr.write("scons: %s: %s\n" % (cmd, e[1])) + # copy child output from tempfiles to our streams + # and do clean up stuff + if stdout is not None and stdoutRedirected == 0: + try: + stdout.write(open( tmpFileStdout, "r" ).read()) + os.remove( tmpFileStdout ) + except (IOError, OSError): + pass + + if stderr is not None and stderrRedirected == 0: + try: + stderr.write(open( tmpFileStderr, "r" ).read()) + os.remove( tmpFileStderr ) + except (IOError, OSError): + pass + return ret + +def exec_spawn(l, env): + try: + result = spawnve(os.P_WAIT, l[0], l, env) + except OSError, e: + try: + result = exitvalmap[e[0]] + sys.stderr.write("scons: %s: %s\n" % (l[0], e[1])) + except KeyError: + result = 127 + if len(l) > 2: + if len(l[2]) < 1000: + command = ' '.join(l[0:3]) + else: + command = l[0] + else: + command = l[0] + sys.stderr.write("scons: unknown OSError exception code %d - '%s': %s\n" % (e[0], command, e[1])) + return result + +def spawn(sh, escape, cmd, args, env): + if not sh: + sys.stderr.write("scons: Could not find command interpreter, is it in your PATH?\n") + return 127 + return exec_spawn([sh, '/C', escape(' '.join(args))], env) + +# Windows does not allow special characters in file names anyway, so no +# need for a complex escape function, we will just quote the arg, except +# that "cmd /c" requires that if an argument ends with a backslash it +# needs to be escaped so as not to interfere with closing double quote +# that we add. +def escape(x): + if x[-1] == '\\': + x = x + '\\' + return '"' + x + '"' + +# Get the windows system directory name +_system_root = None + +def get_system_root(): + global _system_root + if _system_root is not None: + return _system_root + + # A resonable default if we can't read the registry + val = os.environ.get('SystemRoot', "C:\\WINDOWS") + + if SCons.Util.can_read_reg: + try: + # Look for Windows NT system root + k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, + 'Software\\Microsoft\\Windows NT\\CurrentVersion') + val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot') + except SCons.Util.RegError: + try: + # Okay, try the Windows 9x system root + k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, + 'Software\\Microsoft\\Windows\\CurrentVersion') + val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot') + except KeyboardInterrupt: + raise + except: + pass + _system_root = val + return val + +# Get the location of the program files directory +def get_program_files_dir(): + # Now see if we can look in the registry... + val = '' + if SCons.Util.can_read_reg: + try: + # Look for Windows Program Files directory + k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, + 'Software\\Microsoft\\Windows\\CurrentVersion') + val, tok = SCons.Util.RegQueryValueEx(k, 'ProgramFilesDir') + except SCons.Util.RegError: + val = '' + pass + + if val == '': + # A reasonable default if we can't read the registry + # (Actually, it's pretty reasonable even if we can :-) + val = os.path.join(os.path.dirname(get_system_root()),"Program Files") + + return val + + + +# Determine which windows CPU were running on. +class ArchDefinition(object): + """ + A class for defining architecture-specific settings and logic. + """ + def __init__(self, arch, synonyms=[]): + self.arch = arch + self.synonyms = synonyms + +SupportedArchitectureList = [ + ArchDefinition( + 'x86', + ['i386', 'i486', 'i586', 'i686'], + ), + + ArchDefinition( + 'x86_64', + ['AMD64', 'amd64', 'em64t', 'EM64T', 'x86_64'], + ), + + ArchDefinition( + 'ia64', + ['IA64'], + ), +] + +SupportedArchitectureMap = {} +for a in SupportedArchitectureList: + SupportedArchitectureMap[a.arch] = a + for s in a.synonyms: + SupportedArchitectureMap[s] = a + +def get_architecture(arch=None): + """Returns the definition for the specified architecture string. + + If no string is specified, the system default is returned (as defined + by the PROCESSOR_ARCHITEW6432 or PROCESSOR_ARCHITECTURE environment + variables). + """ + if arch is None: + arch = os.environ.get('PROCESSOR_ARCHITEW6432') + if not arch: + arch = os.environ.get('PROCESSOR_ARCHITECTURE') + return SupportedArchitectureMap.get(arch, ArchDefinition('', [''])) + +def generate(env): + # Attempt to find cmd.exe (for WinNT/2k/XP) or + # command.com for Win9x + cmd_interp = '' + # First see if we can look in the registry... + if SCons.Util.can_read_reg: + try: + # Look for Windows NT system root + k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, + 'Software\\Microsoft\\Windows NT\\CurrentVersion') + val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot') + cmd_interp = os.path.join(val, 'System32\\cmd.exe') + except SCons.Util.RegError: + try: + # Okay, try the Windows 9x system root + k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, + 'Software\\Microsoft\\Windows\\CurrentVersion') + val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot') + cmd_interp = os.path.join(val, 'command.com') + except KeyboardInterrupt: + raise + except: + pass + + # For the special case of not having access to the registry, we + # use a temporary path and pathext to attempt to find the command + # interpreter. If we fail, we try to find the interpreter through + # the env's PATH. The problem with that is that it might not + # contain an ENV and a PATH. + if not cmd_interp: + systemroot = get_system_root() + tmp_path = systemroot + os.pathsep + \ + os.path.join(systemroot,'System32') + tmp_pathext = '.com;.exe;.bat;.cmd' + if 'PATHEXT' in os.environ: + tmp_pathext = os.environ['PATHEXT'] + cmd_interp = SCons.Util.WhereIs('cmd', tmp_path, tmp_pathext) + if not cmd_interp: + cmd_interp = SCons.Util.WhereIs('command', tmp_path, tmp_pathext) + + if not cmd_interp: + cmd_interp = env.Detect('cmd') + if not cmd_interp: + cmd_interp = env.Detect('command') + + + if 'ENV' not in env: + env['ENV'] = {} + + # Import things from the external environment to the construction + # environment's ENV. This is a potential slippery slope, because we + # *don't* want to make builds dependent on the user's environment by + # default. We're doing this for SystemRoot, though, because it's + # needed for anything that uses sockets, and seldom changes, and + # for SystemDrive because it's related. + # + # Weigh the impact carefully before adding other variables to this list. + import_env = [ 'SystemDrive', 'SystemRoot', 'TEMP', 'TMP' ] + for var in import_env: + v = os.environ.get(var) + if v: + env['ENV'][var] = v + + if 'COMSPEC' not in env['ENV']: + v = os.environ.get("COMSPEC") + if v: + env['ENV']['COMSPEC'] = v + + env.AppendENVPath('PATH', get_system_root() + '\System32') + + env['ENV']['PATHEXT'] = '.COM;.EXE;.BAT;.CMD' + env['OBJPREFIX'] = '' + env['OBJSUFFIX'] = '.obj' + env['SHOBJPREFIX'] = '$OBJPREFIX' + env['SHOBJSUFFIX'] = '$OBJSUFFIX' + env['PROGPREFIX'] = '' + env['PROGSUFFIX'] = '.exe' + env['LIBPREFIX'] = '' + env['LIBSUFFIX'] = '.lib' + env['SHLIBPREFIX'] = '' + env['SHLIBSUFFIX'] = '.dll' + env['LIBPREFIXES'] = [ '$LIBPREFIX' ] + env['LIBSUFFIXES'] = [ '$LIBSUFFIX' ] + env['PSPAWN'] = piped_spawn + env['SPAWN'] = spawn + env['SHELL'] = cmd_interp + env['TEMPFILE'] = TempFileMunge + env['TEMPFILEPREFIX'] = '@' + env['MAXLINELENGTH'] = 2048 + env['ESCAPE'] = escape + + env['HOST_OS'] = 'win32' + env['HOST_ARCH'] = get_architecture().arch + + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/SConf.py b/scons/scons-local-2.3.0/SCons/SConf.py new file mode 100644 index 000000000..ae51776b6 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/SConf.py @@ -0,0 +1,1030 @@ +"""SCons.SConf + +Autoconf-like configuration support. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/SConf.py 2013/03/03 09:48:35 garyo" + +import SCons.compat + +import io +import os +import re +import sys +import traceback + +import SCons.Action +import SCons.Builder +import SCons.Errors +import SCons.Job +import SCons.Node.FS +import SCons.Taskmaster +import SCons.Util +import SCons.Warnings +import SCons.Conftest + +from SCons.Debug import Trace + +# Turn off the Conftest error logging +SCons.Conftest.LogInputFiles = 0 +SCons.Conftest.LogErrorMessages = 0 + +# Set +build_type = None +build_types = ['clean', 'help'] + +def SetBuildType(type): + global build_type + build_type = type + +# to be set, if we are in dry-run mode +dryrun = 0 + +AUTO=0 # use SCons dependency scanning for up-to-date checks +FORCE=1 # force all tests to be rebuilt +CACHE=2 # force all tests to be taken from cache (raise an error, if necessary) +cache_mode = AUTO + +def SetCacheMode(mode): + """Set the Configure cache mode. mode must be one of "auto", "force", + or "cache".""" + global cache_mode + if mode == "auto": + cache_mode = AUTO + elif mode == "force": + cache_mode = FORCE + elif mode == "cache": + cache_mode = CACHE + else: + raise ValueError("SCons.SConf.SetCacheMode: Unknown mode " + mode) + +progress_display = SCons.Util.display # will be overwritten by SCons.Script +def SetProgressDisplay(display): + """Set the progress display to use (called from SCons.Script)""" + global progress_display + progress_display = display + +SConfFS = None + +_ac_build_counter = 0 # incremented, whenever TryBuild is called +_ac_config_logs = {} # all config.log files created in this build +_ac_config_hs = {} # all config.h files created in this build +sconf_global = None # current sconf object + +def _createConfigH(target, source, env): + t = open(str(target[0]), "w") + defname = re.sub('[^A-Za-z0-9_]', '_', str(target[0]).upper()) + t.write("""#ifndef %(DEFNAME)s_SEEN +#define %(DEFNAME)s_SEEN + +""" % {'DEFNAME' : defname}) + t.write(source[0].get_contents()) + t.write(""" +#endif /* %(DEFNAME)s_SEEN */ +""" % {'DEFNAME' : defname}) + t.close() + +def _stringConfigH(target, source, env): + return "scons: Configure: creating " + str(target[0]) + +def CreateConfigHBuilder(env): + """Called just before the building targets phase begins.""" + if len(_ac_config_hs) == 0: + return + action = SCons.Action.Action(_createConfigH, + _stringConfigH) + sconfigHBld = SCons.Builder.Builder(action=action) + env.Append( BUILDERS={'SConfigHBuilder':sconfigHBld} ) + for k in _ac_config_hs.keys(): + env.SConfigHBuilder(k, env.Value(_ac_config_hs[k])) + +class SConfWarning(SCons.Warnings.Warning): + pass +SCons.Warnings.enableWarningClass(SConfWarning) + +# some error definitions +class SConfError(SCons.Errors.UserError): + def __init__(self,msg): + SCons.Errors.UserError.__init__(self,msg) + +class ConfigureDryRunError(SConfError): + """Raised when a file or directory needs to be updated during a Configure + process, but the user requested a dry-run""" + def __init__(self,target): + if not isinstance(target, SCons.Node.FS.File): + msg = 'Cannot create configure directory "%s" within a dry-run.' % str(target) + else: + msg = 'Cannot update configure test "%s" within a dry-run.' % str(target) + SConfError.__init__(self,msg) + +class ConfigureCacheError(SConfError): + """Raised when a use explicitely requested the cache feature, but the test + is run the first time.""" + def __init__(self,target): + SConfError.__init__(self, '"%s" is not yet built and cache is forced.' % str(target)) + +# define actions for building text files +def _createSource( target, source, env ): + fd = open(str(target[0]), "w") + fd.write(source[0].get_contents()) + fd.close() +def _stringSource( target, source, env ): + return (str(target[0]) + ' <-\n |' + + source[0].get_contents().replace( '\n', "\n |" ) ) + +class SConfBuildInfo(SCons.Node.FS.FileBuildInfo): + """ + Special build info for targets of configure tests. Additional members + are result (did the builder succeed last time?) and string, which + contains messages of the original build phase. + """ + result = None # -> 0/None -> no error, != 0 error + string = None # the stdout / stderr output when building the target + + def set_build_result(self, result, string): + self.result = result + self.string = string + + +class Streamer(object): + """ + 'Sniffer' for a file-like writable object. Similar to the unix tool tee. + """ + def __init__(self, orig): + self.orig = orig + self.s = io.StringIO() + + def write(self, str): + if self.orig: + self.orig.write(str) + self.s.write(str) + + def writelines(self, lines): + for l in lines: + self.write(l + '\n') + + def getvalue(self): + """ + Return everything written to orig since the Streamer was created. + """ + return self.s.getvalue() + + def flush(self): + if self.orig: + self.orig.flush() + self.s.flush() + + +class SConfBuildTask(SCons.Taskmaster.AlwaysTask): + """ + This is almost the same as SCons.Script.BuildTask. Handles SConfErrors + correctly and knows about the current cache_mode. + """ + def display(self, message): + if sconf_global.logstream: + sconf_global.logstream.write("scons: Configure: " + message + "\n") + + def display_cached_string(self, bi): + """ + Logs the original builder messages, given the SConfBuildInfo instance + bi. + """ + if not isinstance(bi, SConfBuildInfo): + SCons.Warnings.warn(SConfWarning, + "The stored build information has an unexpected class: %s" % bi.__class__) + else: + self.display("The original builder output was:\n" + + (" |" + str(bi.string)).replace("\n", "\n |")) + + def failed(self): + # check, if the reason was a ConfigureDryRunError or a + # ConfigureCacheError and if yes, reraise the exception + exc_type = self.exc_info()[0] + if issubclass(exc_type, SConfError): + raise + elif issubclass(exc_type, SCons.Errors.BuildError): + # we ignore Build Errors (occurs, when a test doesn't pass) + # Clear the exception to prevent the contained traceback + # to build a reference cycle. + self.exc_clear() + else: + self.display('Caught exception while building "%s":\n' % + self.targets[0]) + try: + excepthook = sys.excepthook + except AttributeError: + # Earlier versions of Python don't have sys.excepthook... + def excepthook(type, value, tb): + traceback.print_tb(tb) + print type, value + excepthook(*self.exc_info()) + return SCons.Taskmaster.Task.failed(self) + + def collect_node_states(self): + # returns (is_up_to_date, cached_error, cachable) + # where is_up_to_date is 1, if the node(s) are up_to_date + # cached_error is 1, if the node(s) are up_to_date, but the + # build will fail + # cachable is 0, if some nodes are not in our cache + T = 0 + changed = False + cached_error = False + cachable = True + for t in self.targets: + if T: Trace('%s' % (t)) + bi = t.get_stored_info().binfo + if isinstance(bi, SConfBuildInfo): + if T: Trace(': SConfBuildInfo') + if cache_mode == CACHE: + t.set_state(SCons.Node.up_to_date) + if T: Trace(': set_state(up_to-date)') + else: + if T: Trace(': get_state() %s' % t.get_state()) + if T: Trace(': changed() %s' % t.changed()) + if (t.get_state() != SCons.Node.up_to_date and t.changed()): + changed = True + if T: Trace(': changed %s' % changed) + cached_error = cached_error or bi.result + else: + if T: Trace(': else') + # the node hasn't been built in a SConf context or doesn't + # exist + cachable = False + changed = ( t.get_state() != SCons.Node.up_to_date ) + if T: Trace(': changed %s' % changed) + if T: Trace('\n') + return (not changed, cached_error, cachable) + + def execute(self): + if not self.targets[0].has_builder(): + return + + sconf = sconf_global + + is_up_to_date, cached_error, cachable = self.collect_node_states() + + if cache_mode == CACHE and not cachable: + raise ConfigureCacheError(self.targets[0]) + elif cache_mode == FORCE: + is_up_to_date = 0 + + if cached_error and is_up_to_date: + self.display("Building \"%s\" failed in a previous run and all " + "its sources are up to date." % str(self.targets[0])) + binfo = self.targets[0].get_stored_info().binfo + self.display_cached_string(binfo) + raise SCons.Errors.BuildError # will be 'caught' in self.failed + elif is_up_to_date: + self.display("\"%s\" is up to date." % str(self.targets[0])) + binfo = self.targets[0].get_stored_info().binfo + self.display_cached_string(binfo) + elif dryrun: + raise ConfigureDryRunError(self.targets[0]) + else: + # note stdout and stderr are the same here + s = sys.stdout = sys.stderr = Streamer(sys.stdout) + try: + env = self.targets[0].get_build_env() + if cache_mode == FORCE: + # Set up the Decider() to force rebuilds by saying + # that every source has changed. Note that we still + # call the environment's underlying source decider so + # that the correct .sconsign info will get calculated + # and keep the build state consistent. + def force_build(dependency, target, prev_ni, + env_decider=env.decide_source): + env_decider(dependency, target, prev_ni) + return True + if env.decide_source.func_code is not force_build.func_code: + env.Decider(force_build) + env['PSTDOUT'] = env['PSTDERR'] = s + try: + sconf.cached = 0 + self.targets[0].build() + finally: + sys.stdout = sys.stderr = env['PSTDOUT'] = \ + env['PSTDERR'] = sconf.logstream + except KeyboardInterrupt: + raise + except SystemExit: + exc_value = sys.exc_info()[1] + raise SCons.Errors.ExplicitExit(self.targets[0],exc_value.code) + except Exception, e: + for t in self.targets: + binfo = t.get_binfo() + binfo.__class__ = SConfBuildInfo + binfo.set_build_result(1, s.getvalue()) + sconsign_entry = SCons.SConsign.SConsignEntry() + sconsign_entry.binfo = binfo + #sconsign_entry.ninfo = self.get_ninfo() + # We'd like to do this as follows: + # t.store_info(binfo) + # However, we need to store it as an SConfBuildInfo + # object, and store_info() will turn it into a + # regular FileNodeInfo if the target is itself a + # regular File. + sconsign = t.dir.sconsign() + sconsign.set_entry(t.name, sconsign_entry) + sconsign.merge() + raise e + else: + for t in self.targets: + binfo = t.get_binfo() + binfo.__class__ = SConfBuildInfo + binfo.set_build_result(0, s.getvalue()) + sconsign_entry = SCons.SConsign.SConsignEntry() + sconsign_entry.binfo = binfo + #sconsign_entry.ninfo = self.get_ninfo() + # We'd like to do this as follows: + # t.store_info(binfo) + # However, we need to store it as an SConfBuildInfo + # object, and store_info() will turn it into a + # regular FileNodeInfo if the target is itself a + # regular File. + sconsign = t.dir.sconsign() + sconsign.set_entry(t.name, sconsign_entry) + sconsign.merge() + +class SConfBase(object): + """This is simply a class to represent a configure context. After + creating a SConf object, you can call any tests. After finished with your + tests, be sure to call the Finish() method, which returns the modified + environment. + Some words about caching: In most cases, it is not necessary to cache + Test results explicitely. Instead, we use the scons dependency checking + mechanism. For example, if one wants to compile a test program + (SConf.TryLink), the compiler is only called, if the program dependencies + have changed. However, if the program could not be compiled in a former + SConf run, we need to explicitely cache this error. + """ + + def __init__(self, env, custom_tests = {}, conf_dir='$CONFIGUREDIR', + log_file='$CONFIGURELOG', config_h = None, _depth = 0): + """Constructor. Pass additional tests in the custom_tests-dictinary, + e.g. custom_tests={'CheckPrivate':MyPrivateTest}, where MyPrivateTest + defines a custom test. + Note also the conf_dir and log_file arguments (you may want to + build tests in the VariantDir, not in the SourceDir) + """ + global SConfFS + if not SConfFS: + SConfFS = SCons.Node.FS.default_fs or \ + SCons.Node.FS.FS(env.fs.pathTop) + if sconf_global is not None: + raise SCons.Errors.UserError + self.env = env + if log_file is not None: + log_file = SConfFS.File(env.subst(log_file)) + self.logfile = log_file + self.logstream = None + self.lastTarget = None + self.depth = _depth + self.cached = 0 # will be set, if all test results are cached + + # add default tests + default_tests = { + 'CheckCC' : CheckCC, + 'CheckCXX' : CheckCXX, + 'CheckSHCC' : CheckSHCC, + 'CheckSHCXX' : CheckSHCXX, + 'CheckFunc' : CheckFunc, + 'CheckType' : CheckType, + 'CheckTypeSize' : CheckTypeSize, + 'CheckDeclaration' : CheckDeclaration, + 'CheckHeader' : CheckHeader, + 'CheckCHeader' : CheckCHeader, + 'CheckCXXHeader' : CheckCXXHeader, + 'CheckLib' : CheckLib, + 'CheckLibWithHeader' : CheckLibWithHeader, + } + self.AddTests(default_tests) + self.AddTests(custom_tests) + self.confdir = SConfFS.Dir(env.subst(conf_dir)) + if config_h is not None: + config_h = SConfFS.File(config_h) + self.config_h = config_h + self._startup() + + def Finish(self): + """Call this method after finished with your tests: + env = sconf.Finish() + """ + self._shutdown() + return self.env + + def Define(self, name, value = None, comment = None): + """ + Define a pre processor symbol name, with the optional given value in the + current config header. + + If value is None (default), then #define name is written. If value is not + none, then #define name value is written. + + comment is a string which will be put as a C comment in the + header, to explain the meaning of the value (appropriate C comments /* and + */ will be put automatically.""" + lines = [] + if comment: + comment_str = "/* %s */" % comment + lines.append(comment_str) + + if value is not None: + define_str = "#define %s %s" % (name, value) + else: + define_str = "#define %s" % name + lines.append(define_str) + lines.append('') + + self.config_h_text = self.config_h_text + '\n'.join(lines) + + def BuildNodes(self, nodes): + """ + Tries to build the given nodes immediately. Returns 1 on success, + 0 on error. + """ + if self.logstream is not None: + # override stdout / stderr to write in log file + oldStdout = sys.stdout + sys.stdout = self.logstream + oldStderr = sys.stderr + sys.stderr = self.logstream + + # the engine assumes the current path is the SConstruct directory ... + old_fs_dir = SConfFS.getcwd() + old_os_dir = os.getcwd() + SConfFS.chdir(SConfFS.Top, change_os_dir=1) + + # Because we take responsibility here for writing out our + # own .sconsign info (see SConfBuildTask.execute(), above), + # we override the store_info() method with a null place-holder + # so we really control how it gets written. + for n in nodes: + n.store_info = n.do_not_store_info + + ret = 1 + + try: + # ToDo: use user options for calc + save_max_drift = SConfFS.get_max_drift() + SConfFS.set_max_drift(0) + tm = SCons.Taskmaster.Taskmaster(nodes, SConfBuildTask) + # we don't want to build tests in parallel + jobs = SCons.Job.Jobs(1, tm ) + jobs.run() + for n in nodes: + state = n.get_state() + if (state != SCons.Node.executed and + state != SCons.Node.up_to_date): + # the node could not be built. we return 0 in this case + ret = 0 + finally: + SConfFS.set_max_drift(save_max_drift) + os.chdir(old_os_dir) + SConfFS.chdir(old_fs_dir, change_os_dir=0) + if self.logstream is not None: + # restore stdout / stderr + sys.stdout = oldStdout + sys.stderr = oldStderr + return ret + + def pspawn_wrapper(self, sh, escape, cmd, args, env): + """Wrapper function for handling piped spawns. + + This looks to the calling interface (in Action.py) like a "normal" + spawn, but associates the call with the PSPAWN variable from + the construction environment and with the streams to which we + want the output logged. This gets slid into the construction + environment as the SPAWN variable so Action.py doesn't have to + know or care whether it's spawning a piped command or not. + """ + return self.pspawn(sh, escape, cmd, args, env, self.logstream, self.logstream) + + + def TryBuild(self, builder, text = None, extension = ""): + """Low level TryBuild implementation. Normally you don't need to + call that - you can use TryCompile / TryLink / TryRun instead + """ + global _ac_build_counter + + # Make sure we have a PSPAWN value, and save the current + # SPAWN value. + try: + self.pspawn = self.env['PSPAWN'] + except KeyError: + raise SCons.Errors.UserError('Missing PSPAWN construction variable.') + try: + save_spawn = self.env['SPAWN'] + except KeyError: + raise SCons.Errors.UserError('Missing SPAWN construction variable.') + + nodesToBeBuilt = [] + + f = "conftest_" + str(_ac_build_counter) + pref = self.env.subst( builder.builder.prefix ) + suff = self.env.subst( builder.builder.suffix ) + target = self.confdir.File(pref + f + suff) + + try: + # Slide our wrapper into the construction environment as + # the SPAWN function. + self.env['SPAWN'] = self.pspawn_wrapper + sourcetext = self.env.Value(text) + + if text is not None: + textFile = self.confdir.File(f + extension) + textFileNode = self.env.SConfSourceBuilder(target=textFile, + source=sourcetext) + nodesToBeBuilt.extend(textFileNode) + source = textFileNode + else: + source = None + + nodes = builder(target = target, source = source) + if not SCons.Util.is_List(nodes): + nodes = [nodes] + nodesToBeBuilt.extend(nodes) + result = self.BuildNodes(nodesToBeBuilt) + + finally: + self.env['SPAWN'] = save_spawn + + _ac_build_counter = _ac_build_counter + 1 + if result: + self.lastTarget = nodes[0] + else: + self.lastTarget = None + + return result + + def TryAction(self, action, text = None, extension = ""): + """Tries to execute the given action with optional source file + contents and optional source file extension , + Returns the status (0 : failed, 1 : ok) and the contents of the + output file. + """ + builder = SCons.Builder.Builder(action=action) + self.env.Append( BUILDERS = {'SConfActionBuilder' : builder} ) + ok = self.TryBuild(self.env.SConfActionBuilder, text, extension) + del self.env['BUILDERS']['SConfActionBuilder'] + if ok: + outputStr = self.lastTarget.get_contents() + return (1, outputStr) + return (0, "") + + def TryCompile( self, text, extension): + """Compiles the program given in text to an env.Object, using extension + as file extension (e.g. '.c'). Returns 1, if compilation was + successful, 0 otherwise. The target is saved in self.lastTarget (for + further processing). + """ + return self.TryBuild(self.env.Object, text, extension) + + def TryLink( self, text, extension ): + """Compiles the program given in text to an executable env.Program, + using extension as file extension (e.g. '.c'). Returns 1, if + compilation was successful, 0 otherwise. The target is saved in + self.lastTarget (for further processing). + """ + return self.TryBuild(self.env.Program, text, extension ) + + def TryRun(self, text, extension ): + """Compiles and runs the program given in text, using extension + as file extension (e.g. '.c'). Returns (1, outputStr) on success, + (0, '') otherwise. The target (a file containing the program's stdout) + is saved in self.lastTarget (for further processing). + """ + ok = self.TryLink(text, extension) + if( ok ): + prog = self.lastTarget + pname = prog.path + output = self.confdir.File(os.path.basename(pname)+'.out') + node = self.env.Command(output, prog, [ [ pname, ">", "${TARGET}"] ]) + ok = self.BuildNodes(node) + if ok: + outputStr = output.get_contents() + return( 1, outputStr) + return (0, "") + + class TestWrapper(object): + """A wrapper around Tests (to ensure sanity)""" + def __init__(self, test, sconf): + self.test = test + self.sconf = sconf + def __call__(self, *args, **kw): + if not self.sconf.active: + raise SCons.Errors.UserError + context = CheckContext(self.sconf) + ret = self.test(context, *args, **kw) + if self.sconf.config_h is not None: + self.sconf.config_h_text = self.sconf.config_h_text + context.config_h + context.Result("error: no result") + return ret + + def AddTest(self, test_name, test_instance): + """Adds test_class to this SConf instance. It can be called with + self.test_name(...)""" + setattr(self, test_name, SConfBase.TestWrapper(test_instance, self)) + + def AddTests(self, tests): + """Adds all the tests given in the tests dictionary to this SConf + instance + """ + for name in tests.keys(): + self.AddTest(name, tests[name]) + + def _createDir( self, node ): + dirName = str(node) + if dryrun: + if not os.path.isdir( dirName ): + raise ConfigureDryRunError(dirName) + else: + if not os.path.isdir( dirName ): + os.makedirs( dirName ) + node._exists = 1 + + def _startup(self): + """Private method. Set up logstream, and set the environment + variables necessary for a piped build + """ + global _ac_config_logs + global sconf_global + global SConfFS + + self.lastEnvFs = self.env.fs + self.env.fs = SConfFS + self._createDir(self.confdir) + self.confdir.up().add_ignore( [self.confdir] ) + + if self.logfile is not None and not dryrun: + # truncate logfile, if SConf.Configure is called for the first time + # in a build + if self.logfile in _ac_config_logs: + log_mode = "a" + else: + _ac_config_logs[self.logfile] = None + log_mode = "w" + fp = open(str(self.logfile), log_mode) + self.logstream = SCons.Util.Unbuffered(fp) + # logfile may stay in a build directory, so we tell + # the build system not to override it with a eventually + # existing file with the same name in the source directory + self.logfile.dir.add_ignore( [self.logfile] ) + + tb = traceback.extract_stack()[-3-self.depth] + old_fs_dir = SConfFS.getcwd() + SConfFS.chdir(SConfFS.Top, change_os_dir=0) + self.logstream.write('file %s,line %d:\n\tConfigure(confdir = %s)\n' % + (tb[0], tb[1], str(self.confdir)) ) + SConfFS.chdir(old_fs_dir) + else: + self.logstream = None + # we use a special builder to create source files from TEXT + action = SCons.Action.Action(_createSource, + _stringSource) + sconfSrcBld = SCons.Builder.Builder(action=action) + self.env.Append( BUILDERS={'SConfSourceBuilder':sconfSrcBld} ) + self.config_h_text = _ac_config_hs.get(self.config_h, "") + self.active = 1 + # only one SConf instance should be active at a time ... + sconf_global = self + + def _shutdown(self): + """Private method. Reset to non-piped spawn""" + global sconf_global, _ac_config_hs + + if not self.active: + raise SCons.Errors.UserError("Finish may be called only once!") + if self.logstream is not None and not dryrun: + self.logstream.write("\n") + self.logstream.close() + self.logstream = None + # remove the SConfSourceBuilder from the environment + blds = self.env['BUILDERS'] + del blds['SConfSourceBuilder'] + self.env.Replace( BUILDERS=blds ) + self.active = 0 + sconf_global = None + if not self.config_h is None: + _ac_config_hs[self.config_h] = self.config_h_text + self.env.fs = self.lastEnvFs + +class CheckContext(object): + """Provides a context for configure tests. Defines how a test writes to the + screen and log file. + + A typical test is just a callable with an instance of CheckContext as + first argument: + + def CheckCustom(context, ...) + context.Message('Checking my weird test ... ') + ret = myWeirdTestFunction(...) + context.Result(ret) + + Often, myWeirdTestFunction will be one of + context.TryCompile/context.TryLink/context.TryRun. The results of + those are cached, for they are only rebuild, if the dependencies have + changed. + """ + + def __init__(self, sconf): + """Constructor. Pass the corresponding SConf instance.""" + self.sconf = sconf + self.did_show_result = 0 + + # for Conftest.py: + self.vardict = {} + self.havedict = {} + self.headerfilename = None + self.config_h = "" # config_h text will be stored here + # we don't regenerate the config.h file after each test. That means, + # that tests won't be able to include the config.h file, and so + # they can't do an #ifdef HAVE_XXX_H. This shouldn't be a major + # issue, though. If it turns out, that we need to include config.h + # in tests, we must ensure, that the dependencies are worked out + # correctly. Note that we can't use Conftest.py's support for config.h, + # cause we will need to specify a builder for the config.h file ... + + def Message(self, text): + """Inform about what we are doing right now, e.g. + 'Checking for SOMETHING ... ' + """ + self.Display(text) + self.sconf.cached = 1 + self.did_show_result = 0 + + def Result(self, res): + """Inform about the result of the test. res may be an integer or a + string. In case of an integer, the written text will be 'yes' or 'no'. + The result is only displayed when self.did_show_result is not set. + """ + if isinstance(res, (int, bool)): + if res: + text = "yes" + else: + text = "no" + elif isinstance(res, str): + text = res + else: + raise TypeError("Expected string, int or bool, got " + str(type(res))) + + if self.did_show_result == 0: + # Didn't show result yet, do it now. + self.Display(text + "\n") + self.did_show_result = 1 + + def TryBuild(self, *args, **kw): + return self.sconf.TryBuild(*args, **kw) + + def TryAction(self, *args, **kw): + return self.sconf.TryAction(*args, **kw) + + def TryCompile(self, *args, **kw): + return self.sconf.TryCompile(*args, **kw) + + def TryLink(self, *args, **kw): + return self.sconf.TryLink(*args, **kw) + + def TryRun(self, *args, **kw): + return self.sconf.TryRun(*args, **kw) + + def __getattr__( self, attr ): + if( attr == 'env' ): + return self.sconf.env + elif( attr == 'lastTarget' ): + return self.sconf.lastTarget + else: + raise AttributeError("CheckContext instance has no attribute '%s'" % attr) + + #### Stuff used by Conftest.py (look there for explanations). + + def BuildProg(self, text, ext): + self.sconf.cached = 1 + # TODO: should use self.vardict for $CC, $CPPFLAGS, etc. + return not self.TryBuild(self.env.Program, text, ext) + + def CompileProg(self, text, ext): + self.sconf.cached = 1 + # TODO: should use self.vardict for $CC, $CPPFLAGS, etc. + return not self.TryBuild(self.env.Object, text, ext) + + def CompileSharedObject(self, text, ext): + self.sconf.cached = 1 + # TODO: should use self.vardict for $SHCC, $CPPFLAGS, etc. + return not self.TryBuild(self.env.SharedObject, text, ext) + + def RunProg(self, text, ext): + self.sconf.cached = 1 + # TODO: should use self.vardict for $CC, $CPPFLAGS, etc. + st, out = self.TryRun(text, ext) + return not st, out + + def AppendLIBS(self, lib_name_list): + oldLIBS = self.env.get( 'LIBS', [] ) + self.env.Append(LIBS = lib_name_list) + return oldLIBS + + def PrependLIBS(self, lib_name_list): + oldLIBS = self.env.get( 'LIBS', [] ) + self.env.Prepend(LIBS = lib_name_list) + return oldLIBS + + def SetLIBS(self, val): + oldLIBS = self.env.get( 'LIBS', [] ) + self.env.Replace(LIBS = val) + return oldLIBS + + def Display(self, msg): + if self.sconf.cached: + # We assume that Display is called twice for each test here + # once for the Checking for ... message and once for the result. + # The self.sconf.cached flag can only be set between those calls + msg = "(cached) " + msg + self.sconf.cached = 0 + progress_display(msg, append_newline=0) + self.Log("scons: Configure: " + msg + "\n") + + def Log(self, msg): + if self.sconf.logstream is not None: + self.sconf.logstream.write(msg) + + #### End of stuff used by Conftest.py. + + +def SConf(*args, **kw): + if kw.get(build_type, True): + kw['_depth'] = kw.get('_depth', 0) + 1 + for bt in build_types: + try: + del kw[bt] + except KeyError: + pass + return SConfBase(*args, **kw) + else: + return SCons.Util.Null() + + +def CheckFunc(context, function_name, header = None, language = None): + res = SCons.Conftest.CheckFunc(context, function_name, header = header, language = language) + context.did_show_result = 1 + return not res + +def CheckType(context, type_name, includes = "", language = None): + res = SCons.Conftest.CheckType(context, type_name, + header = includes, language = language) + context.did_show_result = 1 + return not res + +def CheckTypeSize(context, type_name, includes = "", language = None, expect = None): + res = SCons.Conftest.CheckTypeSize(context, type_name, + header = includes, language = language, + expect = expect) + context.did_show_result = 1 + return res + +def CheckDeclaration(context, declaration, includes = "", language = None): + res = SCons.Conftest.CheckDeclaration(context, declaration, + includes = includes, + language = language) + context.did_show_result = 1 + return not res + +def createIncludesFromHeaders(headers, leaveLast, include_quotes = '""'): + # used by CheckHeader and CheckLibWithHeader to produce C - #include + # statements from the specified header (list) + if not SCons.Util.is_List(headers): + headers = [headers] + l = [] + if leaveLast: + lastHeader = headers[-1] + headers = headers[:-1] + else: + lastHeader = None + for s in headers: + l.append("#include %s%s%s\n" + % (include_quotes[0], s, include_quotes[1])) + return ''.join(l), lastHeader + +def CheckHeader(context, header, include_quotes = '<>', language = None): + """ + A test for a C or C++ header file. + """ + prog_prefix, hdr_to_check = \ + createIncludesFromHeaders(header, 1, include_quotes) + res = SCons.Conftest.CheckHeader(context, hdr_to_check, prog_prefix, + language = language, + include_quotes = include_quotes) + context.did_show_result = 1 + return not res + +def CheckCC(context): + res = SCons.Conftest.CheckCC(context) + context.did_show_result = 1 + return not res + +def CheckCXX(context): + res = SCons.Conftest.CheckCXX(context) + context.did_show_result = 1 + return not res + +def CheckSHCC(context): + res = SCons.Conftest.CheckSHCC(context) + context.did_show_result = 1 + return not res + +def CheckSHCXX(context): + res = SCons.Conftest.CheckSHCXX(context) + context.did_show_result = 1 + return not res + +# Bram: Make this function obsolete? CheckHeader() is more generic. + +def CheckCHeader(context, header, include_quotes = '""'): + """ + A test for a C header file. + """ + return CheckHeader(context, header, include_quotes, language = "C") + + +# Bram: Make this function obsolete? CheckHeader() is more generic. + +def CheckCXXHeader(context, header, include_quotes = '""'): + """ + A test for a C++ header file. + """ + return CheckHeader(context, header, include_quotes, language = "C++") + + +def CheckLib(context, library = None, symbol = "main", + header = None, language = None, autoadd = 1): + """ + A test for a library. See also CheckLibWithHeader. + Note that library may also be None to test whether the given symbol + compiles without flags. + """ + + if library == []: + library = [None] + + if not SCons.Util.is_List(library): + library = [library] + + # ToDo: accept path for the library + res = SCons.Conftest.CheckLib(context, library, symbol, header = header, + language = language, autoadd = autoadd) + context.did_show_result = 1 + return not res + +# XXX +# Bram: Can only include one header and can't use #ifdef HAVE_HEADER_H. + +def CheckLibWithHeader(context, libs, header, language, + call = None, autoadd = 1): + # ToDo: accept path for library. Support system header files. + """ + Another (more sophisticated) test for a library. + Checks, if library and header is available for language (may be 'C' + or 'CXX'). Call maybe be a valid expression _with_ a trailing ';'. + As in CheckLib, we support library=None, to test if the call compiles + without extra link flags. + """ + prog_prefix, dummy = \ + createIncludesFromHeaders(header, 0) + if libs == []: + libs = [None] + + if not SCons.Util.is_List(libs): + libs = [libs] + + res = SCons.Conftest.CheckLib(context, libs, None, prog_prefix, + call = call, language = language, autoadd = autoadd) + context.did_show_result = 1 + return not res + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/SConsign.py b/scons/scons-local-2.3.0/SCons/SConsign.py new file mode 100644 index 000000000..a11f4e480 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/SConsign.py @@ -0,0 +1,389 @@ +"""SCons.SConsign + +Writing and reading information to the .sconsign file or files. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/SConsign.py 2013/03/03 09:48:35 garyo" + +import SCons.compat + +import os +# compat layer imports "cPickle" for us if it's available. +import pickle + +import SCons.dblite +import SCons.Warnings + +def corrupt_dblite_warning(filename): + SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning, + "Ignoring corrupt .sconsign file: %s"%filename) + +SCons.dblite.ignore_corrupt_dbfiles = 1 +SCons.dblite.corruption_warning = corrupt_dblite_warning + +#XXX Get rid of the global array so this becomes re-entrant. +sig_files = [] + +# Info for the database SConsign implementation (now the default): +# "DataBase" is a dictionary that maps top-level SConstruct directories +# to open database handles. +# "DB_Module" is the Python database module to create the handles. +# "DB_Name" is the base name of the database file (minus any +# extension the underlying DB module will add). +DataBase = {} +DB_Module = SCons.dblite +DB_Name = ".sconsign" +DB_sync_list = [] + +def Get_DataBase(dir): + global DataBase, DB_Module, DB_Name + top = dir.fs.Top + if not os.path.isabs(DB_Name) and top.repositories: + mode = "c" + for d in [top] + top.repositories: + if dir.is_under(d): + try: + return DataBase[d], mode + except KeyError: + path = d.entry_abspath(DB_Name) + try: db = DataBase[d] = DB_Module.open(path, mode) + except (IOError, OSError): pass + else: + if mode != "r": + DB_sync_list.append(db) + return db, mode + mode = "r" + try: + return DataBase[top], "c" + except KeyError: + db = DataBase[top] = DB_Module.open(DB_Name, "c") + DB_sync_list.append(db) + return db, "c" + except TypeError: + print "DataBase =", DataBase + raise + +def Reset(): + """Reset global state. Used by unit tests that end up using + SConsign multiple times to get a clean slate for each test.""" + global sig_files, DB_sync_list + sig_files = [] + DB_sync_list = [] + +normcase = os.path.normcase + +def write(): + global sig_files + for sig_file in sig_files: + sig_file.write(sync=0) + for db in DB_sync_list: + try: + syncmethod = db.sync + except AttributeError: + pass # Not all dbm modules have sync() methods. + else: + syncmethod() + try: + closemethod = db.close + except AttributeError: + pass # Not all dbm modules have close() methods. + else: + closemethod() + +class SConsignEntry(object): + """ + Wrapper class for the generic entry in a .sconsign file. + The Node subclass populates it with attributes as it pleases. + + XXX As coded below, we do expect a '.binfo' attribute to be added, + but we'll probably generalize this in the next refactorings. + """ + current_version_id = 1 + def __init__(self): + # Create an object attribute from the class attribute so it ends up + # in the pickled data in the .sconsign file. + _version_id = self.current_version_id + def convert_to_sconsign(self): + self.binfo.convert_to_sconsign() + def convert_from_sconsign(self, dir, name): + self.binfo.convert_from_sconsign(dir, name) + +class Base(object): + """ + This is the controlling class for the signatures for the collection of + entries associated with a specific directory. The actual directory + association will be maintained by a subclass that is specific to + the underlying storage method. This class provides a common set of + methods for fetching and storing the individual bits of information + that make up signature entry. + """ + def __init__(self): + self.entries = {} + self.dirty = False + self.to_be_merged = {} + + def get_entry(self, filename): + """ + Fetch the specified entry attribute. + """ + return self.entries[filename] + + def set_entry(self, filename, obj): + """ + Set the entry. + """ + self.entries[filename] = obj + self.dirty = True + + def do_not_set_entry(self, filename, obj): + pass + + def store_info(self, filename, node): + entry = node.get_stored_info() + entry.binfo.merge(node.get_binfo()) + self.to_be_merged[filename] = node + self.dirty = True + + def do_not_store_info(self, filename, node): + pass + + def merge(self): + for key, node in self.to_be_merged.items(): + entry = node.get_stored_info() + try: + ninfo = entry.ninfo + except AttributeError: + # This happens with SConf Nodes, because the configuration + # subsystem takes direct control over how the build decision + # is made and its information stored. + pass + else: + ninfo.merge(node.get_ninfo()) + self.entries[key] = entry + self.to_be_merged = {} + +class DB(Base): + """ + A Base subclass that reads and writes signature information + from a global .sconsign.db* file--the actual file suffix is + determined by the database module. + """ + def __init__(self, dir): + Base.__init__(self) + + self.dir = dir + + db, mode = Get_DataBase(dir) + + # Read using the path relative to the top of the Repository + # (self.dir.tpath) from which we're fetching the signature + # information. + path = normcase(dir.tpath) + try: + rawentries = db[path] + except KeyError: + pass + else: + try: + self.entries = pickle.loads(rawentries) + if not isinstance(self.entries, dict): + self.entries = {} + raise TypeError + except KeyboardInterrupt: + raise + except Exception, e: + SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning, + "Ignoring corrupt sconsign entry : %s (%s)\n"%(self.dir.tpath, e)) + for key, entry in self.entries.items(): + entry.convert_from_sconsign(dir, key) + + if mode == "r": + # This directory is actually under a repository, which means + # likely they're reaching in directly for a dependency on + # a file there. Don't actually set any entry info, so we + # won't try to write to that .sconsign.dblite file. + self.set_entry = self.do_not_set_entry + self.store_info = self.do_not_store_info + + global sig_files + sig_files.append(self) + + def write(self, sync=1): + if not self.dirty: + return + + self.merge() + + db, mode = Get_DataBase(self.dir) + + # Write using the path relative to the top of the SConstruct + # directory (self.dir.path), not relative to the top of + # the Repository; we only write to our own .sconsign file, + # not to .sconsign files in Repositories. + path = normcase(self.dir.path) + for key, entry in self.entries.items(): + entry.convert_to_sconsign() + db[path] = pickle.dumps(self.entries, 1) + + if sync: + try: + syncmethod = db.sync + except AttributeError: + # Not all anydbm modules have sync() methods. + pass + else: + syncmethod() + +class Dir(Base): + def __init__(self, fp=None, dir=None): + """ + fp - file pointer to read entries from + """ + Base.__init__(self) + + if not fp: + return + + self.entries = pickle.load(fp) + if not isinstance(self.entries, dict): + self.entries = {} + raise TypeError + + if dir: + for key, entry in self.entries.items(): + entry.convert_from_sconsign(dir, key) + +class DirFile(Dir): + """ + Encapsulates reading and writing a per-directory .sconsign file. + """ + def __init__(self, dir): + """ + dir - the directory for the file + """ + + self.dir = dir + self.sconsign = os.path.join(dir.path, '.sconsign') + + try: + fp = open(self.sconsign, 'rb') + except IOError: + fp = None + + try: + Dir.__init__(self, fp, dir) + except KeyboardInterrupt: + raise + except: + SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning, + "Ignoring corrupt .sconsign file: %s"%self.sconsign) + + global sig_files + sig_files.append(self) + + def write(self, sync=1): + """ + Write the .sconsign file to disk. + + Try to write to a temporary file first, and rename it if we + succeed. If we can't write to the temporary file, it's + probably because the directory isn't writable (and if so, + how did we build anything in this directory, anyway?), so + try to write directly to the .sconsign file as a backup. + If we can't rename, try to copy the temporary contents back + to the .sconsign file. Either way, always try to remove + the temporary file at the end. + """ + if not self.dirty: + return + + self.merge() + + temp = os.path.join(self.dir.path, '.scons%d' % os.getpid()) + try: + file = open(temp, 'wb') + fname = temp + except IOError: + try: + file = open(self.sconsign, 'wb') + fname = self.sconsign + except IOError: + return + for key, entry in self.entries.items(): + entry.convert_to_sconsign() + pickle.dump(self.entries, file, 1) + file.close() + if fname != self.sconsign: + try: + mode = os.stat(self.sconsign)[0] + os.chmod(self.sconsign, 0666) + os.unlink(self.sconsign) + except (IOError, OSError): + # Try to carry on in the face of either OSError + # (things like permission issues) or IOError (disk + # or network issues). If there's a really dangerous + # issue, it should get re-raised by the calls below. + pass + try: + os.rename(fname, self.sconsign) + except OSError: + # An OSError failure to rename may indicate something + # like the directory has no write permission, but + # the .sconsign file itself might still be writable, + # so try writing on top of it directly. An IOError + # here, or in any of the following calls, would get + # raised, indicating something like a potentially + # serious disk or network issue. + open(self.sconsign, 'wb').write(open(fname, 'rb').read()) + os.chmod(self.sconsign, mode) + try: + os.unlink(temp) + except (IOError, OSError): + pass + +ForDirectory = DB + +def File(name, dbm_module=None): + """ + Arrange for all signatures to be stored in a global .sconsign.db* + file. + """ + global ForDirectory, DB_Name, DB_Module + if name is None: + ForDirectory = DirFile + DB_Module = None + else: + ForDirectory = DB + DB_Name = name + if not dbm_module is None: + DB_Module = dbm_module + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Scanner/C.py b/scons/scons-local-2.3.0/SCons/Scanner/C.py new file mode 100644 index 000000000..0b664a008 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Scanner/C.py @@ -0,0 +1,132 @@ +"""SCons.Scanner.C + +This module implements the depenency scanner for C/C++ code. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Scanner/C.py 2013/03/03 09:48:35 garyo" + +import SCons.Node.FS +import SCons.Scanner +import SCons.Util + +import SCons.cpp + +class SConsCPPScanner(SCons.cpp.PreProcessor): + """ + SCons-specific subclass of the cpp.py module's processing. + + We subclass this so that: 1) we can deal with files represented + by Nodes, not strings; 2) we can keep track of the files that are + missing. + """ + def __init__(self, *args, **kw): + SCons.cpp.PreProcessor.__init__(self, *args, **kw) + self.missing = [] + def initialize_result(self, fname): + self.result = SCons.Util.UniqueList([fname]) + def finalize_result(self, fname): + return self.result[1:] + def find_include_file(self, t): + keyword, quote, fname = t + result = SCons.Node.FS.find_file(fname, self.searchpath[quote]) + if not result: + self.missing.append((fname, self.current_file)) + return result + def read_file(self, file): + try: + fp = open(str(file.rfile())) + except EnvironmentError, e: + self.missing.append((file, self.current_file)) + return '' + else: + return fp.read() + +def dictify_CPPDEFINES(env): + cppdefines = env.get('CPPDEFINES', {}) + if cppdefines is None: + return {} + if SCons.Util.is_Sequence(cppdefines): + result = {} + for c in cppdefines: + if SCons.Util.is_Sequence(c): + result[c[0]] = c[1] + else: + result[c] = None + return result + if not SCons.Util.is_Dict(cppdefines): + return {cppdefines : None} + return cppdefines + +class SConsCPPScannerWrapper(object): + """ + The SCons wrapper around a cpp.py scanner. + + This is the actual glue between the calling conventions of generic + SCons scanners, and the (subclass of) cpp.py class that knows how + to look for #include lines with reasonably real C-preprocessor-like + evaluation of #if/#ifdef/#else/#elif lines. + """ + def __init__(self, name, variable): + self.name = name + self.path = SCons.Scanner.FindPathDirs(variable) + def __call__(self, node, env, path = ()): + cpp = SConsCPPScanner(current = node.get_dir(), + cpppath = path, + dict = dictify_CPPDEFINES(env)) + result = cpp(node) + for included, includer in cpp.missing: + fmt = "No dependency generated for file: %s (included from: %s) -- file not found" + SCons.Warnings.warn(SCons.Warnings.DependencyWarning, + fmt % (included, includer)) + return result + + def recurse_nodes(self, nodes): + return nodes + def select(self, node): + return self + +def CScanner(): + """Return a prototype Scanner instance for scanning source files + that use the C pre-processor""" + + # Here's how we would (or might) use the CPP scanner code above that + # knows how to evaluate #if/#ifdef/#else/#elif lines when searching + # for #includes. This is commented out for now until we add the + # right configurability to let users pick between the scanners. + #return SConsCPPScannerWrapper("CScanner", "CPPPATH") + + cs = SCons.Scanner.ClassicCPP("CScanner", + "$CPPSUFFIXES", + "CPPPATH", + '^[ \t]*#[ \t]*(?:include|import)[ \t]*(<|")([^>"]+)(>|")') + return cs + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Scanner/D.py b/scons/scons-local-2.3.0/SCons/Scanner/D.py new file mode 100644 index 000000000..18b530f3a --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Scanner/D.py @@ -0,0 +1,73 @@ +"""SCons.Scanner.D + +Scanner for the Digital Mars "D" programming language. + +Coded by Andy Friesen +17 Nov 2003 + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Scanner/D.py 2013/03/03 09:48:35 garyo" + +import re + +import SCons.Scanner + +def DScanner(): + """Return a prototype Scanner instance for scanning D source files""" + ds = D() + return ds + +class D(SCons.Scanner.Classic): + def __init__ (self): + SCons.Scanner.Classic.__init__ (self, + name = "DScanner", + suffixes = '$DSUFFIXES', + path_variable = 'DPATH', + regex = 'import\s+(?:[a-zA-Z0-9_.]+)\s*(?:,\s*(?:[a-zA-Z0-9_.]+)\s*)*;') + + self.cre2 = re.compile ('(?:import\s)?\s*([a-zA-Z0-9_.]+)\s*(?:,|;)', re.M) + + def find_include(self, include, source_dir, path): + # translate dots (package separators) to slashes + inc = include.replace('.', '/') + + i = SCons.Node.FS.find_file(inc + '.d', (source_dir,) + path) + if i is None: + i = SCons.Node.FS.find_file (inc + '.di', (source_dir,) + path) + return i, include + + def find_include_names(self, node): + includes = [] + for i in self.cre.findall(node.get_text_contents()): + includes = includes + self.cre2.findall(i) + return includes + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Scanner/Dir.py b/scons/scons-local-2.3.0/SCons/Scanner/Dir.py new file mode 100644 index 000000000..f508a6fe0 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Scanner/Dir.py @@ -0,0 +1,109 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Scanner/Dir.py 2013/03/03 09:48:35 garyo" + +import SCons.Node.FS +import SCons.Scanner + +def only_dirs(nodes): + is_Dir = lambda n: isinstance(n.disambiguate(), SCons.Node.FS.Dir) + return list(filter(is_Dir, nodes)) + +def DirScanner(**kw): + """Return a prototype Scanner instance for scanning + directories for on-disk files""" + kw['node_factory'] = SCons.Node.FS.Entry + kw['recursive'] = only_dirs + return SCons.Scanner.Base(scan_on_disk, "DirScanner", **kw) + +def DirEntryScanner(**kw): + """Return a prototype Scanner instance for "scanning" + directory Nodes for their in-memory entries""" + kw['node_factory'] = SCons.Node.FS.Entry + kw['recursive'] = None + return SCons.Scanner.Base(scan_in_memory, "DirEntryScanner", **kw) + +skip_entry = {} + +skip_entry_list = [ + '.', + '..', + '.sconsign', + # Used by the native dblite.py module. + '.sconsign.dblite', + # Used by dbm and dumbdbm. + '.sconsign.dir', + # Used by dbm. + '.sconsign.pag', + # Used by dumbdbm. + '.sconsign.dat', + '.sconsign.bak', + # Used by some dbm emulations using Berkeley DB. + '.sconsign.db', +] + +for skip in skip_entry_list: + skip_entry[skip] = 1 + skip_entry[SCons.Node.FS._my_normcase(skip)] = 1 + +do_not_scan = lambda k: k not in skip_entry + +def scan_on_disk(node, env, path=()): + """ + Scans a directory for on-disk files and directories therein. + + Looking up the entries will add these to the in-memory Node tree + representation of the file system, so all we have to do is just + that and then call the in-memory scanning function. + """ + try: + flist = node.fs.listdir(node.abspath) + except (IOError, OSError): + return [] + e = node.Entry + for f in filter(do_not_scan, flist): + # Add ./ to the beginning of the file name so if it begins with a + # '#' we don't look it up relative to the top-level directory. + e('./' + f) + return scan_in_memory(node, env, path) + +def scan_in_memory(node, env, path=()): + """ + "Scans" a Node.FS.Dir for its in-memory entries. + """ + try: + entries = node.entries + except AttributeError: + # It's not a Node.FS.Dir (or doesn't look enough like one for + # our purposes), which can happen if a target list containing + # mixed Node types (Dirs and Files, for example) has a Dir as + # the first entry. + return [] + entry_list = sorted(filter(do_not_scan, list(entries.keys()))) + return [entries[n] for n in entry_list] + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Scanner/Fortran.py b/scons/scons-local-2.3.0/SCons/Scanner/Fortran.py new file mode 100644 index 000000000..390e95172 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Scanner/Fortran.py @@ -0,0 +1,316 @@ +"""SCons.Scanner.Fortran + +This module implements the dependency scanner for Fortran code. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Scanner/Fortran.py 2013/03/03 09:48:35 garyo" + +import re + +import SCons.Node +import SCons.Node.FS +import SCons.Scanner +import SCons.Util +import SCons.Warnings + +class F90Scanner(SCons.Scanner.Classic): + """ + A Classic Scanner subclass for Fortran source files which takes + into account both USE and INCLUDE statements. This scanner will + work for both F77 and F90 (and beyond) compilers. + + Currently, this scanner assumes that the include files do not contain + USE statements. To enable the ability to deal with USE statements + in include files, add logic right after the module names are found + to loop over each include file, search for and locate each USE + statement, and append each module name to the list of dependencies. + Caching the search results in a common dictionary somewhere so that + the same include file is not searched multiple times would be a + smart thing to do. + """ + + def __init__(self, name, suffixes, path_variable, + use_regex, incl_regex, def_regex, *args, **kw): + + self.cre_use = re.compile(use_regex, re.M) + self.cre_incl = re.compile(incl_regex, re.M) + self.cre_def = re.compile(def_regex, re.M) + + def _scan(node, env, path, self=self): + node = node.rfile() + + if not node.exists(): + return [] + + return self.scan(node, env, path) + + kw['function'] = _scan + kw['path_function'] = SCons.Scanner.FindPathDirs(path_variable) + kw['recursive'] = 1 + kw['skeys'] = suffixes + kw['name'] = name + + SCons.Scanner.Current.__init__(self, *args, **kw) + + def scan(self, node, env, path=()): + + # cache the includes list in node so we only scan it once: + if node.includes != None: + mods_and_includes = node.includes + else: + # retrieve all included filenames + includes = self.cre_incl.findall(node.get_text_contents()) + # retrieve all USE'd module names + modules = self.cre_use.findall(node.get_text_contents()) + # retrieve all defined module names + defmodules = self.cre_def.findall(node.get_text_contents()) + + # Remove all USE'd module names that are defined in the same file + # (case-insensitively) + d = {} + for m in defmodules: + d[m.lower()] = 1 + modules = [m for m in modules if m.lower() not in d] + + # Convert module name to a .mod filename + suffix = env.subst('$FORTRANMODSUFFIX') + modules = [x.lower() + suffix for x in modules] + # Remove unique items from the list + mods_and_includes = SCons.Util.unique(includes+modules) + node.includes = mods_and_includes + + # This is a hand-coded DSU (decorate-sort-undecorate, or + # Schwartzian transform) pattern. The sort key is the raw name + # of the file as specifed on the USE or INCLUDE line, which lets + # us keep the sort order constant regardless of whether the file + # is actually found in a Repository or locally. + nodes = [] + source_dir = node.get_dir() + if callable(path): + path = path() + for dep in mods_and_includes: + n, i = self.find_include(dep, source_dir, path) + + if n is None: + SCons.Warnings.warn(SCons.Warnings.DependencyWarning, + "No dependency generated for file: %s (referenced by: %s) -- file not found" % (i, node)) + else: + sortkey = self.sort_key(dep) + nodes.append((sortkey, n)) + + return [pair[1] for pair in sorted(nodes)] + +def FortranScan(path_variable="FORTRANPATH"): + """Return a prototype Scanner instance for scanning source files + for Fortran USE & INCLUDE statements""" + +# The USE statement regex matches the following: +# +# USE module_name +# USE :: module_name +# USE, INTRINSIC :: module_name +# USE, NON_INTRINSIC :: module_name +# +# Limitations +# +# -- While the regex can handle multiple USE statements on one line, +# it cannot properly handle them if they are commented out. +# In either of the following cases: +# +# ! USE mod_a ; USE mod_b [entire line is commented out] +# USE mod_a ! ; USE mod_b [in-line comment of second USE statement] +# +# the second module name (mod_b) will be picked up as a dependency +# even though it should be ignored. The only way I can see +# to rectify this would be to modify the scanner to eliminate +# the call to re.findall, read in the contents of the file, +# treating the comment character as an end-of-line character +# in addition to the normal linefeed, loop over each line, +# weeding out the comments, and looking for the USE statements. +# One advantage to this is that the regex passed to the scanner +# would no longer need to match a semicolon. +# +# -- I question whether or not we need to detect dependencies to +# INTRINSIC modules because these are built-in to the compiler. +# If we consider them a dependency, will SCons look for them, not +# find them, and kill the build? Or will we there be standard +# compiler-specific directories we will need to point to so the +# compiler and SCons can locate the proper object and mod files? + +# Here is a breakdown of the regex: +# +# (?i) : regex is case insensitive +# ^ : start of line +# (?: : group a collection of regex symbols without saving the match as a "group" +# ^|; : matches either the start of the line or a semicolon - semicolon +# ) : end the unsaved grouping +# \s* : any amount of white space +# USE : match the string USE, case insensitive +# (?: : group a collection of regex symbols without saving the match as a "group" +# \s+| : match one or more whitespace OR .... (the next entire grouped set of regex symbols) +# (?: : group a collection of regex symbols without saving the match as a "group" +# (?: : establish another unsaved grouping of regex symbols +# \s* : any amount of white space +# , : match a comma +# \s* : any amount of white space +# (?:NON_)? : optionally match the prefix NON_, case insensitive +# INTRINSIC : match the string INTRINSIC, case insensitive +# )? : optionally match the ", INTRINSIC/NON_INTRINSIC" grouped expression +# \s* : any amount of white space +# :: : match a double colon that must appear after the INTRINSIC/NON_INTRINSIC attribute +# ) : end the unsaved grouping +# ) : end the unsaved grouping +# \s* : match any amount of white space +# (\w+) : match the module name that is being USE'd +# +# + use_regex = "(?i)(?:^|;)\s*USE(?:\s+|(?:(?:\s*,\s*(?:NON_)?INTRINSIC)?\s*::))\s*(\w+)" + + +# The INCLUDE statement regex matches the following: +# +# INCLUDE 'some_Text' +# INCLUDE "some_Text" +# INCLUDE "some_Text" ; INCLUDE "some_Text" +# INCLUDE kind_"some_Text" +# INCLUDE kind_'some_Text" +# +# where some_Text can include any alphanumeric and/or special character +# as defined by the Fortran 2003 standard. +# +# Limitations: +# +# -- The Fortran standard dictates that a " or ' in the INCLUDE'd +# string must be represented as a "" or '', if the quotes that wrap +# the entire string are either a ' or ", respectively. While the +# regular expression below can detect the ' or " characters just fine, +# the scanning logic, presently is unable to detect them and reduce +# them to a single instance. This probably isn't an issue since, +# in practice, ' or " are not generally used in filenames. +# +# -- This regex will not properly deal with multiple INCLUDE statements +# when the entire line has been commented out, ala +# +# ! INCLUDE 'some_file' ; INCLUDE 'some_file' +# +# In such cases, it will properly ignore the first INCLUDE file, +# but will actually still pick up the second. Interestingly enough, +# the regex will properly deal with these cases: +# +# INCLUDE 'some_file' +# INCLUDE 'some_file' !; INCLUDE 'some_file' +# +# To get around the above limitation, the FORTRAN programmer could +# simply comment each INCLUDE statement separately, like this +# +# ! INCLUDE 'some_file' !; INCLUDE 'some_file' +# +# The way I see it, the only way to get around this limitation would +# be to modify the scanning logic to replace the calls to re.findall +# with a custom loop that processes each line separately, throwing +# away fully commented out lines before attempting to match against +# the INCLUDE syntax. +# +# Here is a breakdown of the regex: +# +# (?i) : regex is case insensitive +# (?: : begin a non-saving group that matches the following: +# ^ : either the start of the line +# | : or +# ['">]\s*; : a semicolon that follows a single quote, +# double quote or greater than symbol (with any +# amount of whitespace in between). This will +# allow the regex to match multiple INCLUDE +# statements per line (although it also requires +# the positive lookahead assertion that is +# used below). It will even properly deal with +# (i.e. ignore) cases in which the additional +# INCLUDES are part of an in-line comment, ala +# " INCLUDE 'someFile' ! ; INCLUDE 'someFile2' " +# ) : end of non-saving group +# \s* : any amount of white space +# INCLUDE : match the string INCLUDE, case insensitive +# \s+ : match one or more white space characters +# (?\w+_)? : match the optional "kind-param _" prefix allowed by the standard +# [<"'] : match the include delimiter - an apostrophe, double quote, or less than symbol +# (.+?) : match one or more characters that make up +# the included path and file name and save it +# in a group. The Fortran standard allows for +# any non-control character to be used. The dot +# operator will pick up any character, including +# control codes, but I can't conceive of anyone +# putting control codes in their file names. +# The question mark indicates it is non-greedy so +# that regex will match only up to the next quote, +# double quote, or greater than symbol +# (?=["'>]) : positive lookahead assertion to match the include +# delimiter - an apostrophe, double quote, or +# greater than symbol. This level of complexity +# is required so that the include delimiter is +# not consumed by the match, thus allowing the +# sub-regex discussed above to uniquely match a +# set of semicolon-separated INCLUDE statements +# (as allowed by the F2003 standard) + + include_regex = """(?i)(?:^|['">]\s*;)\s*INCLUDE\s+(?:\w+_)?[<"'](.+?)(?=["'>])""" + +# The MODULE statement regex finds module definitions by matching +# the following: +# +# MODULE module_name +# +# but *not* the following: +# +# MODULE PROCEDURE procedure_name +# +# Here is a breakdown of the regex: +# +# (?i) : regex is case insensitive +# ^\s* : any amount of white space +# MODULE : match the string MODULE, case insensitive +# \s+ : match one or more white space characters +# (?!PROCEDURE) : but *don't* match if the next word matches +# PROCEDURE (negative lookahead assertion), +# case insensitive +# (\w+) : match one or more alphanumeric characters +# that make up the defined module name and +# save it in a group + + def_regex = """(?i)^\s*MODULE\s+(?!PROCEDURE)(\w+)""" + + scanner = F90Scanner("FortranScan", + "$FORTRANSUFFIXES", + path_variable, + use_regex, + include_regex, + def_regex) + return scanner + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Scanner/IDL.py b/scons/scons-local-2.3.0/SCons/Scanner/IDL.py new file mode 100644 index 000000000..13a0226b9 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Scanner/IDL.py @@ -0,0 +1,48 @@ +"""SCons.Scanner.IDL + +This module implements the depenency scanner for IDL (Interface +Definition Language) files. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Scanner/IDL.py 2013/03/03 09:48:35 garyo" + +import SCons.Node.FS +import SCons.Scanner + +def IDLScan(): + """Return a prototype Scanner instance for scanning IDL source files""" + cs = SCons.Scanner.ClassicCPP("IDLScan", + "$IDLSUFFIXES", + "CPPPATH", + '^[ \t]*(?:#[ \t]*include|[ \t]*import)[ \t]+(<|")([^>"]+)(>|")') + return cs + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Scanner/LaTeX.py b/scons/scons-local-2.3.0/SCons/Scanner/LaTeX.py new file mode 100644 index 000000000..c61688bc3 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Scanner/LaTeX.py @@ -0,0 +1,390 @@ +"""SCons.Scanner.LaTeX + +This module implements the dependency scanner for LaTeX code. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Scanner/LaTeX.py 2013/03/03 09:48:35 garyo" + +import os.path +import re + +import SCons.Scanner +import SCons.Util + +# list of graphics file extensions for TeX and LaTeX +TexGraphics = ['.eps', '.ps'] +LatexGraphics = ['.pdf', '.png', '.jpg', '.gif', '.tif'] + +# Used as a return value of modify_env_var if the variable is not set. +class _Null(object): + pass +_null = _Null + +# The user specifies the paths in env[variable], similar to other builders. +# They may be relative and must be converted to absolute, as expected +# by LaTeX and Co. The environment may already have some paths in +# env['ENV'][var]. These paths are honored, but the env[var] paths have +# higher precedence. All changes are un-done on exit. +def modify_env_var(env, var, abspath): + try: + save = env['ENV'][var] + except KeyError: + save = _null + env.PrependENVPath(var, abspath) + try: + if SCons.Util.is_List(env[var]): + env.PrependENVPath(var, [os.path.abspath(str(p)) for p in env[var]]) + else: + # Split at os.pathsep to convert into absolute path + env.PrependENVPath(var, [os.path.abspath(p) for p in str(env[var]).split(os.pathsep)]) + except KeyError: + pass + + # Convert into a string explicitly to append ":" (without which it won't search system + # paths as well). The problem is that env.AppendENVPath(var, ":") + # does not work, refuses to append ":" (os.pathsep). + + if SCons.Util.is_List(env['ENV'][var]): + env['ENV'][var] = os.pathsep.join(env['ENV'][var]) + # Append the trailing os.pathsep character here to catch the case with no env[var] + env['ENV'][var] = env['ENV'][var] + os.pathsep + + return save + +class FindENVPathDirs(object): + """A class to bind a specific *PATH variable name to a function that + will return all of the *path directories.""" + def __init__(self, variable): + self.variable = variable + def __call__(self, env, dir=None, target=None, source=None, argument=None): + import SCons.PathList + try: + path = env['ENV'][self.variable] + except KeyError: + return () + + dir = dir or env.fs._cwd + path = SCons.PathList.PathList(path).subst_path(env, target, source) + return tuple(dir.Rfindalldirs(path)) + + + +def LaTeXScanner(): + """Return a prototype Scanner instance for scanning LaTeX source files + when built with latex. + """ + ds = LaTeX(name = "LaTeXScanner", + suffixes = '$LATEXSUFFIXES', + # in the search order, see below in LaTeX class docstring + graphics_extensions = TexGraphics, + recursive = 0) + return ds + +def PDFLaTeXScanner(): + """Return a prototype Scanner instance for scanning LaTeX source files + when built with pdflatex. + """ + ds = LaTeX(name = "PDFLaTeXScanner", + suffixes = '$LATEXSUFFIXES', + # in the search order, see below in LaTeX class docstring + graphics_extensions = LatexGraphics, + recursive = 0) + return ds + +class LaTeX(SCons.Scanner.Base): + """Class for scanning LaTeX files for included files. + + Unlike most scanners, which use regular expressions that just + return the included file name, this returns a tuple consisting + of the keyword for the inclusion ("include", "includegraphics", + "input", or "bibliography"), and then the file name itself. + Based on a quick look at LaTeX documentation, it seems that we + should append .tex suffix for the "include" keywords, append .tex if + there is no extension for the "input" keyword, and need to add .bib + for the "bibliography" keyword that does not accept extensions by itself. + + Finally, if there is no extension for an "includegraphics" keyword + latex will append .ps or .eps to find the file, while pdftex may use .pdf, + .jpg, .tif, .mps, or .png. + + The actual subset and search order may be altered by + DeclareGraphicsExtensions command. This complication is ignored. + The default order corresponds to experimentation with teTeX + $ latex --version + pdfeTeX 3.141592-1.21a-2.2 (Web2C 7.5.4) + kpathsea version 3.5.4 + The order is: + ['.eps', '.ps'] for latex + ['.png', '.pdf', '.jpg', '.tif']. + + Another difference is that the search path is determined by the type + of the file being searched: + env['TEXINPUTS'] for "input" and "include" keywords + env['TEXINPUTS'] for "includegraphics" keyword + env['TEXINPUTS'] for "lstinputlisting" keyword + env['BIBINPUTS'] for "bibliography" keyword + env['BSTINPUTS'] for "bibliographystyle" keyword + env['INDEXSTYLE'] for "makeindex" keyword, no scanning support needed + just allows user to set it if needed. + + FIXME: also look for the class or style in document[class|style]{} + FIXME: also look for the argument of bibliographystyle{} + """ + keyword_paths = {'include': 'TEXINPUTS', + 'input': 'TEXINPUTS', + 'includegraphics': 'TEXINPUTS', + 'bibliography': 'BIBINPUTS', + 'bibliographystyle': 'BSTINPUTS', + 'addbibresource': 'BIBINPUTS', + 'addglobalbib': 'BIBINPUTS', + 'addsectionbib': 'BIBINPUTS', + 'makeindex': 'INDEXSTYLE', + 'usepackage': 'TEXINPUTS', + 'lstinputlisting': 'TEXINPUTS'} + env_variables = SCons.Util.unique(list(keyword_paths.values())) + + def __init__(self, name, suffixes, graphics_extensions, *args, **kw): + + # We have to include \n with the % we exclude from the first part + # part of the regex because the expression is compiled with re.M. + # Without the \n, the ^ could match the beginning of a *previous* + # line followed by one or more newline characters (i.e. blank + # lines), interfering with a match on the next line. + # add option for whitespace before the '[options]' or the '{filename}' + regex = r'^[^%\n]*\\(include|includegraphics(?:\s*\[[^\]]+\])?|lstinputlisting(?:\[[^\]]+\])?|input|bibliography|addbibresource|addglobalbib|addsectionbib|usepackage)\s*{([^}]*)}' + self.cre = re.compile(regex, re.M) + self.comment_re = re.compile(r'^((?:(?:\\%)|[^%\n])*)(.*)$', re.M) + + self.graphics_extensions = graphics_extensions + + def _scan(node, env, path=(), self=self): + node = node.rfile() + if not node.exists(): + return [] + return self.scan_recurse(node, path) + + class FindMultiPathDirs(object): + """The stock FindPathDirs function has the wrong granularity: + it is called once per target, while we need the path that depends + on what kind of included files is being searched. This wrapper + hides multiple instances of FindPathDirs, one per the LaTeX path + variable in the environment. When invoked, the function calculates + and returns all the required paths as a dictionary (converted into + a tuple to become hashable). Then the scan function converts it + back and uses a dictionary of tuples rather than a single tuple + of paths. + """ + def __init__(self, dictionary): + self.dictionary = {} + for k,n in dictionary.items(): + self.dictionary[k] = ( SCons.Scanner.FindPathDirs(n), + FindENVPathDirs(n) ) + + def __call__(self, env, dir=None, target=None, source=None, + argument=None): + di = {} + for k,(c,cENV) in self.dictionary.items(): + di[k] = ( c(env, dir=None, target=None, source=None, + argument=None) , + cENV(env, dir=None, target=None, source=None, + argument=None) ) + # To prevent "dict is not hashable error" + return tuple(di.items()) + + class LaTeXScanCheck(object): + """Skip all but LaTeX source files, i.e., do not scan *.eps, + *.pdf, *.jpg, etc. + """ + def __init__(self, suffixes): + self.suffixes = suffixes + def __call__(self, node, env): + current = not node.has_builder() or node.is_up_to_date() + scannable = node.get_suffix() in env.subst_list(self.suffixes)[0] + # Returning false means that the file is not scanned. + return scannable and current + + kw['function'] = _scan + kw['path_function'] = FindMultiPathDirs(LaTeX.keyword_paths) + kw['recursive'] = 0 + kw['skeys'] = suffixes + kw['scan_check'] = LaTeXScanCheck(suffixes) + kw['name'] = name + + SCons.Scanner.Base.__init__(self, *args, **kw) + + def _latex_names(self, include): + filename = include[1] + if include[0] == 'input': + base, ext = os.path.splitext( filename ) + if ext == "": + return [filename + '.tex'] + if (include[0] == 'include'): + return [filename + '.tex'] + if include[0] == 'bibliography': + base, ext = os.path.splitext( filename ) + if ext == "": + return [filename + '.bib'] + if include[0] == 'usepackage': + base, ext = os.path.splitext( filename ) + if ext == "": + return [filename + '.sty'] + if include[0] == 'includegraphics': + base, ext = os.path.splitext( filename ) + if ext == "": + #return [filename+e for e in self.graphics_extensions + TexGraphics] + # use the line above to find dependencies for the PDF builder + # when only an .eps figure is present. Since it will be found + # if the user tells scons how to make the pdf figure, leave + # it out for now. + return [filename+e for e in self.graphics_extensions] + return [filename] + + def sort_key(self, include): + return SCons.Node.FS._my_normcase(str(include)) + + def find_include(self, include, source_dir, path): + try: + sub_path = path[include[0]] + except (IndexError, KeyError): + sub_path = () + try_names = self._latex_names(include) + for n in try_names: + # see if we find it using the path in env[var] + i = SCons.Node.FS.find_file(n, (source_dir,) + sub_path[0]) + if i: + return i, include + # see if we find it using the path in env['ENV'][var] + i = SCons.Node.FS.find_file(n, (source_dir,) + sub_path[1]) + if i: + return i, include + return i, include + + def canonical_text(self, text): + """Standardize an input TeX-file contents. + + Currently: + * removes comments, unwrapping comment-wrapped lines. + """ + out = [] + line_continues_a_comment = False + for line in text.splitlines(): + line,comment = self.comment_re.findall(line)[0] + if line_continues_a_comment == True: + out[-1] = out[-1] + line.lstrip() + else: + out.append(line) + line_continues_a_comment = len(comment) > 0 + return '\n'.join(out).rstrip()+'\n' + + def scan(self, node): + # Modify the default scan function to allow for the regular + # expression to return a comma separated list of file names + # as can be the case with the bibliography keyword. + + # Cache the includes list in node so we only scan it once: + # path_dict = dict(list(path)) + # add option for whitespace (\s) before the '[' + noopt_cre = re.compile('\s*\[.*$') + if node.includes != None: + includes = node.includes + else: + text = self.canonical_text(node.get_text_contents()) + includes = self.cre.findall(text) + # 1. Split comma-separated lines, e.g. + # ('bibliography', 'phys,comp') + # should become two entries + # ('bibliography', 'phys') + # ('bibliography', 'comp') + # 2. Remove the options, e.g., such as + # ('includegraphics[clip,width=0.7\\linewidth]', 'picture.eps') + # should become + # ('includegraphics', 'picture.eps') + split_includes = [] + for include in includes: + inc_type = noopt_cre.sub('', include[0]) + inc_list = include[1].split(',') + for j in range(len(inc_list)): + split_includes.append( (inc_type, inc_list[j]) ) + # + includes = split_includes + node.includes = includes + + return includes + + def scan_recurse(self, node, path=()): + """ do a recursive scan of the top level target file + This lets us search for included files based on the + directory of the main file just as latex does""" + + path_dict = dict(list(path)) + + queue = [] + queue.extend( self.scan(node) ) + seen = {} + + # This is a hand-coded DSU (decorate-sort-undecorate, or + # Schwartzian transform) pattern. The sort key is the raw name + # of the file as specifed on the \include, \input, etc. line. + # TODO: what about the comment in the original Classic scanner: + # """which lets + # us keep the sort order constant regardless of whether the file + # is actually found in a Repository or locally.""" + nodes = [] + source_dir = node.get_dir() + #for include in includes: + while queue: + + include = queue.pop() + try: + if seen[include[1]] == 1: + continue + except KeyError: + seen[include[1]] = 1 + + # + # Handle multiple filenames in include[1] + # + n, i = self.find_include(include, source_dir, path_dict) + if n is None: + # Do not bother with 'usepackage' warnings, as they most + # likely refer to system-level files + if include[0] != 'usepackage': + SCons.Warnings.warn(SCons.Warnings.DependencyWarning, + "No dependency generated for file: %s (included from: %s) -- file not found" % (i, node)) + else: + sortkey = self.sort_key(n) + nodes.append((sortkey, n)) + # recurse down + queue.extend( self.scan(n) ) + + return [pair[1] for pair in sorted(nodes)] + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Scanner/Prog.py b/scons/scons-local-2.3.0/SCons/Scanner/Prog.py new file mode 100644 index 000000000..d7e19d36d --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Scanner/Prog.py @@ -0,0 +1,101 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Scanner/Prog.py 2013/03/03 09:48:35 garyo" + +import SCons.Node +import SCons.Node.FS +import SCons.Scanner +import SCons.Util + +# global, set by --debug=findlibs +print_find_libs = None + +def ProgramScanner(**kw): + """Return a prototype Scanner instance for scanning executable + files for static-lib dependencies""" + kw['path_function'] = SCons.Scanner.FindPathDirs('LIBPATH') + ps = SCons.Scanner.Base(scan, "ProgramScanner", **kw) + return ps + +def scan(node, env, libpath = ()): + """ + This scanner scans program files for static-library + dependencies. It will search the LIBPATH environment variable + for libraries specified in the LIBS variable, returning any + files it finds as dependencies. + """ + try: + libs = env['LIBS'] + except KeyError: + # There are no LIBS in this environment, so just return a null list: + return [] + if SCons.Util.is_String(libs): + libs = libs.split() + else: + libs = SCons.Util.flatten(libs) + + try: + prefix = env['LIBPREFIXES'] + if not SCons.Util.is_List(prefix): + prefix = [ prefix ] + except KeyError: + prefix = [ '' ] + + try: + suffix = env['LIBSUFFIXES'] + if not SCons.Util.is_List(suffix): + suffix = [ suffix ] + except KeyError: + suffix = [ '' ] + + pairs = [] + for suf in map(env.subst, suffix): + for pref in map(env.subst, prefix): + pairs.append((pref, suf)) + + result = [] + + if callable(libpath): + libpath = libpath() + + find_file = SCons.Node.FS.find_file + adjustixes = SCons.Util.adjustixes + for lib in libs: + if SCons.Util.is_String(lib): + lib = env.subst(lib) + for pref, suf in pairs: + l = adjustixes(lib, pref, suf) + l = find_file(l, libpath, verbose=print_find_libs) + if l: + result.append(l) + else: + result.append(lib) + + return result + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Scanner/RC.py b/scons/scons-local-2.3.0/SCons/Scanner/RC.py new file mode 100644 index 000000000..3fa3e6757 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Scanner/RC.py @@ -0,0 +1,55 @@ +"""SCons.Scanner.RC + +This module implements the depenency scanner for RC (Interface +Definition Language) files. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Scanner/RC.py 2013/03/03 09:48:35 garyo" + +import SCons.Node.FS +import SCons.Scanner +import re + +def RCScan(): + """Return a prototype Scanner instance for scanning RC source files""" + + res_re= r'^(?:\s*#\s*(?:include)|' \ + '.*?\s+(?:ICON|BITMAP|CURSOR|HTML|FONT|MESSAGETABLE|TYPELIB|REGISTRY|D3DFX)' \ + '\s*.*?)' \ + '\s*(<|"| )([^>"\s]+)(?:[>"\s])*$' + resScanner = SCons.Scanner.ClassicCPP( "ResourceScanner", + "$RCSUFFIXES", + "CPPPATH", + res_re ) + + return resScanner + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Scanner/__init__.py b/scons/scons-local-2.3.0/SCons/Scanner/__init__.py new file mode 100644 index 000000000..b7b3197a4 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Scanner/__init__.py @@ -0,0 +1,413 @@ +"""SCons.Scanner + +The Scanner package for the SCons software construction utility. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Scanner/__init__.py 2013/03/03 09:48:35 garyo" + +import re + +import SCons.Node.FS +import SCons.Util + + +class _Null(object): + pass + +# This is used instead of None as a default argument value so None can be +# used as an actual argument value. +_null = _Null + +def Scanner(function, *args, **kw): + """ + Public interface factory function for creating different types + of Scanners based on the different types of "functions" that may + be supplied. + + TODO: Deprecate this some day. We've moved the functionality + inside the Base class and really don't need this factory function + any more. It was, however, used by some of our Tool modules, so + the call probably ended up in various people's custom modules + patterned on SCons code. + """ + if SCons.Util.is_Dict(function): + return Selector(function, *args, **kw) + else: + return Base(function, *args, **kw) + + + +class FindPathDirs(object): + """A class to bind a specific *PATH variable name to a function that + will return all of the *path directories.""" + def __init__(self, variable): + self.variable = variable + def __call__(self, env, dir=None, target=None, source=None, argument=None): + import SCons.PathList + try: + path = env[self.variable] + except KeyError: + return () + + dir = dir or env.fs._cwd + path = SCons.PathList.PathList(path).subst_path(env, target, source) + return tuple(dir.Rfindalldirs(path)) + + + +class Base(object): + """ + The base class for dependency scanners. This implements + straightforward, single-pass scanning of a single file. + """ + + def __init__(self, + function, + name = "NONE", + argument = _null, + skeys = _null, + path_function = None, + # Node.FS.Base so that, by default, it's okay for a + # scanner to return a Dir, File or Entry. + node_class = SCons.Node.FS.Base, + node_factory = None, + scan_check = None, + recursive = None): + """ + Construct a new scanner object given a scanner function. + + 'function' - a scanner function taking two or three + arguments and returning a list of strings. + + 'name' - a name for identifying this scanner object. + + 'argument' - an optional argument that, if specified, will be + passed to both the scanner function and the path_function. + + 'skeys' - an optional list argument that can be used to determine + which scanner should be used for a given Node. In the case of File + nodes, for example, the 'skeys' would be file suffixes. + + 'path_function' - a function that takes four or five arguments + (a construction environment, Node for the directory containing + the SConscript file that defined the primary target, list of + target nodes, list of source nodes, and optional argument for + this instance) and returns a tuple of the directories that can + be searched for implicit dependency files. May also return a + callable() which is called with no args and returns the tuple + (supporting Bindable class). + + 'node_class' - the class of Nodes which this scan will return. + If node_class is None, then this scanner will not enforce any + Node conversion and will return the raw results from the + underlying scanner function. + + 'node_factory' - the factory function to be called to translate + the raw results returned by the scanner function into the + expected node_class objects. + + 'scan_check' - a function to be called to first check whether + this node really needs to be scanned. + + 'recursive' - specifies that this scanner should be invoked + recursively on all of the implicit dependencies it returns + (the canonical example being #include lines in C source files). + May be a callable, which will be called to filter the list + of nodes found to select a subset for recursive scanning + (the canonical example being only recursively scanning + subdirectories within a directory). + + The scanner function's first argument will be a Node that should + be scanned for dependencies, the second argument will be an + Environment object, the third argument will be the tuple of paths + returned by the path_function, and the fourth argument will be + the value passed into 'argument', and the returned list should + contain the Nodes for all the direct dependencies of the file. + + Examples: + + s = Scanner(my_scanner_function) + + s = Scanner(function = my_scanner_function) + + s = Scanner(function = my_scanner_function, argument = 'foo') + + """ + + # Note: this class could easily work with scanner functions that take + # something other than a filename as an argument (e.g. a database + # node) and a dependencies list that aren't file names. All that + # would need to be changed is the documentation. + + self.function = function + self.path_function = path_function + self.name = name + self.argument = argument + + if skeys is _null: + if SCons.Util.is_Dict(function): + skeys = list(function.keys()) + else: + skeys = [] + self.skeys = skeys + + self.node_class = node_class + self.node_factory = node_factory + self.scan_check = scan_check + if callable(recursive): + self.recurse_nodes = recursive + elif recursive: + self.recurse_nodes = self._recurse_all_nodes + else: + self.recurse_nodes = self._recurse_no_nodes + + def path(self, env, dir=None, target=None, source=None): + if not self.path_function: + return () + if not self.argument is _null: + return self.path_function(env, dir, target, source, self.argument) + else: + return self.path_function(env, dir, target, source) + + def __call__(self, node, env, path = ()): + """ + This method scans a single object. 'node' is the node + that will be passed to the scanner function, and 'env' is the + environment that will be passed to the scanner function. A list of + direct dependency nodes for the specified node will be returned. + """ + if self.scan_check and not self.scan_check(node, env): + return [] + + self = self.select(node) + + if not self.argument is _null: + list = self.function(node, env, path, self.argument) + else: + list = self.function(node, env, path) + + kw = {} + if hasattr(node, 'dir'): + kw['directory'] = node.dir + node_factory = env.get_factory(self.node_factory) + nodes = [] + for l in list: + if self.node_class and not isinstance(l, self.node_class): + l = node_factory(l, **kw) + nodes.append(l) + return nodes + + def __cmp__(self, other): + try: + return cmp(self.__dict__, other.__dict__) + except AttributeError: + # other probably doesn't have a __dict__ + return cmp(self.__dict__, other) + + def __hash__(self): + return id(self) + + def __str__(self): + return self.name + + def add_skey(self, skey): + """Add a skey to the list of skeys""" + self.skeys.append(skey) + + def get_skeys(self, env=None): + if env and SCons.Util.is_String(self.skeys): + return env.subst_list(self.skeys)[0] + return self.skeys + + def select(self, node): + if SCons.Util.is_Dict(self.function): + key = node.scanner_key() + try: + return self.function[key] + except KeyError: + return None + else: + return self + + def _recurse_all_nodes(self, nodes): + return nodes + + def _recurse_no_nodes(self, nodes): + return [] + + recurse_nodes = _recurse_no_nodes + + def add_scanner(self, skey, scanner): + self.function[skey] = scanner + self.add_skey(skey) + + +class Selector(Base): + """ + A class for selecting a more specific scanner based on the + scanner_key() (suffix) for a specific Node. + + TODO: This functionality has been moved into the inner workings of + the Base class, and this class will be deprecated at some point. + (It was never exposed directly as part of the public interface, + although it is used by the Scanner() factory function that was + used by various Tool modules and therefore was likely a template + for custom modules that may be out there.) + """ + def __init__(self, dict, *args, **kw): + Base.__init__(self, None, *args, **kw) + self.dict = dict + self.skeys = list(dict.keys()) + + def __call__(self, node, env, path = ()): + return self.select(node)(node, env, path) + + def select(self, node): + try: + return self.dict[node.scanner_key()] + except KeyError: + return None + + def add_scanner(self, skey, scanner): + self.dict[skey] = scanner + self.add_skey(skey) + + +class Current(Base): + """ + A class for scanning files that are source files (have no builder) + or are derived files and are current (which implies that they exist, + either locally or in a repository). + """ + + def __init__(self, *args, **kw): + def current_check(node, env): + return not node.has_builder() or node.is_up_to_date() + kw['scan_check'] = current_check + Base.__init__(self, *args, **kw) + +class Classic(Current): + """ + A Scanner subclass to contain the common logic for classic CPP-style + include scanning, but which can be customized to use different + regular expressions to find the includes. + + Note that in order for this to work "out of the box" (without + overriding the find_include() and sort_key() methods), the regular + expression passed to the constructor must return the name of the + include file in group 0. + """ + + def __init__(self, name, suffixes, path_variable, regex, *args, **kw): + + self.cre = re.compile(regex, re.M) + + def _scan(node, env, path=(), self=self): + node = node.rfile() + if not node.exists(): + return [] + return self.scan(node, path) + + kw['function'] = _scan + kw['path_function'] = FindPathDirs(path_variable) + kw['recursive'] = 1 + kw['skeys'] = suffixes + kw['name'] = name + + Current.__init__(self, *args, **kw) + + def find_include(self, include, source_dir, path): + n = SCons.Node.FS.find_file(include, (source_dir,) + tuple(path)) + return n, include + + def sort_key(self, include): + return SCons.Node.FS._my_normcase(include) + + def find_include_names(self, node): + return self.cre.findall(node.get_text_contents()) + + def scan(self, node, path=()): + + # cache the includes list in node so we only scan it once: + if node.includes is not None: + includes = node.includes + else: + includes = self.find_include_names (node) + # Intern the names of the include files. Saves some memory + # if the same header is included many times. + node.includes = list(map(SCons.Util.silent_intern, includes)) + + # This is a hand-coded DSU (decorate-sort-undecorate, or + # Schwartzian transform) pattern. The sort key is the raw name + # of the file as specifed on the #include line (including the + # " or <, since that may affect what file is found), which lets + # us keep the sort order constant regardless of whether the file + # is actually found in a Repository or locally. + nodes = [] + source_dir = node.get_dir() + if callable(path): + path = path() + for include in includes: + n, i = self.find_include(include, source_dir, path) + + if n is None: + SCons.Warnings.warn(SCons.Warnings.DependencyWarning, + "No dependency generated for file: %s (included from: %s) -- file not found" % (i, node)) + else: + nodes.append((self.sort_key(include), n)) + + return [pair[1] for pair in sorted(nodes)] + +class ClassicCPP(Classic): + """ + A Classic Scanner subclass which takes into account the type of + bracketing used to include the file, and uses classic CPP rules + for searching for the files based on the bracketing. + + Note that in order for this to work, the regular expression passed + to the constructor must return the leading bracket in group 0, and + the contained filename in group 1. + """ + def find_include(self, include, source_dir, path): + if include[0] == '"': + paths = (source_dir,) + tuple(path) + else: + paths = tuple(path) + (source_dir,) + + n = SCons.Node.FS.find_file(include[1], paths) + + i = SCons.Util.silent_intern(include[1]) + return n, i + + def sort_key(self, include): + return SCons.Node.FS._my_normcase(' '.join(include)) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Script/Interactive.py b/scons/scons-local-2.3.0/SCons/Script/Interactive.py new file mode 100644 index 000000000..b9a2e1e7c --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Script/Interactive.py @@ -0,0 +1,384 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Script/Interactive.py 2013/03/03 09:48:35 garyo" + +__doc__ = """ +SCons interactive mode +""" + +# TODO: +# +# This has the potential to grow into something with a really big life +# of its own, which might or might not be a good thing. Nevertheless, +# here are some enhancements that will probably be requested some day +# and are worth keeping in mind (assuming this takes off): +# +# - A command to re-read / re-load the SConscript files. This may +# involve allowing people to specify command-line options (e.g. -f, +# -I, --no-site-dir) that affect how the SConscript files are read. +# +# - Additional command-line options on the "build" command. +# +# Of the supported options that seemed to make sense (after a quick +# pass through the list), the ones that seemed likely enough to be +# used are listed in the man page and have explicit test scripts. +# +# These had code changed in Script/Main.py to support them, but didn't +# seem likely to be used regularly, so had no test scripts added: +# +# build --diskcheck=* +# build --implicit-cache=* +# build --implicit-deps-changed=* +# build --implicit-deps-unchanged=* +# +# These look like they should "just work" with no changes to the +# existing code, but like those above, look unlikely to be used and +# therefore had no test scripts added: +# +# build --random +# +# These I'm not sure about. They might be useful for individual +# "build" commands, and may even work, but they seem unlikely enough +# that we'll wait until they're requested before spending any time on +# writing test scripts for them, or investigating whether they work. +# +# build -q [??? is there a useful analog to the exit status?] +# build --duplicate= +# build --profile= +# build --max-drift= +# build --warn=* +# build --Y +# +# - Most of the SCons command-line options that the "build" command +# supports should be settable as default options that apply to all +# subsequent "build" commands. Maybe a "set {option}" command that +# maps to "SetOption('{option}')". +# +# - Need something in the 'help' command that prints the -h output. +# +# - A command to run the configure subsystem separately (must see how +# this interacts with the new automake model). +# +# - Command-line completion of target names; maybe even of SCons options? +# Completion is something that's supported by the Python cmd module, +# so this should be doable without too much trouble. +# + +import cmd +import copy +import os +import re +import shlex +import sys + +try: + import readline +except ImportError: + pass + +class SConsInteractiveCmd(cmd.Cmd): + """\ + build [TARGETS] Build the specified TARGETS and their dependencies. + 'b' is a synonym. + clean [TARGETS] Clean (remove) the specified TARGETS and their + dependencies. 'c' is a synonym. + exit Exit SCons interactive mode. + help [COMMAND] Prints help for the specified COMMAND. 'h' and + '?' are synonyms. + shell [COMMANDLINE] Execute COMMANDLINE in a subshell. 'sh' and '!' + are synonyms. + version Prints SCons version information. + """ + + synonyms = { + 'b' : 'build', + 'c' : 'clean', + 'h' : 'help', + 'scons' : 'build', + 'sh' : 'shell', + } + + def __init__(self, **kw): + cmd.Cmd.__init__(self) + for key, val in kw.items(): + setattr(self, key, val) + + if sys.platform == 'win32': + self.shell_variable = 'COMSPEC' + else: + self.shell_variable = 'SHELL' + + def default(self, argv): + print "*** Unknown command: %s" % argv[0] + + def onecmd(self, line): + line = line.strip() + if not line: + print self.lastcmd + return self.emptyline() + self.lastcmd = line + if line[0] == '!': + line = 'shell ' + line[1:] + elif line[0] == '?': + line = 'help ' + line[1:] + if os.sep == '\\': + line = line.replace('\\', '\\\\') + argv = shlex.split(line) + argv[0] = self.synonyms.get(argv[0], argv[0]) + if not argv[0]: + return self.default(line) + else: + try: + func = getattr(self, 'do_' + argv[0]) + except AttributeError: + return self.default(argv) + return func(argv) + + def do_build(self, argv): + """\ + build [TARGETS] Build the specified TARGETS and their + dependencies. 'b' is a synonym. + """ + import SCons.Node + import SCons.SConsign + import SCons.Script.Main + + options = copy.deepcopy(self.options) + + options, targets = self.parser.parse_args(argv[1:], values=options) + + SCons.Script.COMMAND_LINE_TARGETS = targets + + if targets: + SCons.Script.BUILD_TARGETS = targets + else: + # If the user didn't specify any targets on the command line, + # use the list of default targets. + SCons.Script.BUILD_TARGETS = SCons.Script._build_plus_default + + nodes = SCons.Script.Main._build_targets(self.fs, + options, + targets, + self.target_top) + + if not nodes: + return + + # Call each of the Node's alter_targets() methods, which may + # provide additional targets that ended up as part of the build + # (the canonical example being a VariantDir() when we're building + # from a source directory) and which we therefore need their + # state cleared, too. + x = [] + for n in nodes: + x.extend(n.alter_targets()[0]) + nodes.extend(x) + + # Clean up so that we can perform the next build correctly. + # + # We do this by walking over all the children of the targets, + # and clearing their state. + # + # We currently have to re-scan each node to find their + # children, because built nodes have already been partially + # cleared and don't remember their children. (In scons + # 0.96.1 and earlier, this wasn't the case, and we didn't + # have to re-scan the nodes.) + # + # Because we have to re-scan each node, we can't clear the + # nodes as we walk over them, because we may end up rescanning + # a cleared node as we scan a later node. Therefore, only + # store the list of nodes that need to be cleared as we walk + # the tree, and clear them in a separate pass. + # + # XXX: Someone more familiar with the inner workings of scons + # may be able to point out a more efficient way to do this. + + SCons.Script.Main.progress_display("scons: Clearing cached node information ...") + + seen_nodes = {} + + def get_unseen_children(node, parent, seen_nodes=seen_nodes): + def is_unseen(node, seen_nodes=seen_nodes): + return node not in seen_nodes + return list(filter(is_unseen, node.children(scan=1))) + + def add_to_seen_nodes(node, parent, seen_nodes=seen_nodes): + seen_nodes[node] = 1 + + # If this file is in a VariantDir and has a + # corresponding source file in the source tree, remember the + # node in the source tree, too. This is needed in + # particular to clear cached implicit dependencies on the + # source file, since the scanner will scan it if the + # VariantDir was created with duplicate=0. + try: + rfile_method = node.rfile + except AttributeError: + return + else: + rfile = rfile_method() + if rfile != node: + seen_nodes[rfile] = 1 + + for node in nodes: + walker = SCons.Node.Walker(node, + kids_func=get_unseen_children, + eval_func=add_to_seen_nodes) + n = walker.get_next() + while n: + n = walker.get_next() + + for node in seen_nodes.keys(): + # Call node.clear() to clear most of the state + node.clear() + # node.clear() doesn't reset node.state, so call + # node.set_state() to reset it manually + node.set_state(SCons.Node.no_state) + node.implicit = None + + # Debug: Uncomment to verify that all Taskmaster reference + # counts have been reset to zero. + #if node.ref_count != 0: + # from SCons.Debug import Trace + # Trace('node %s, ref_count %s !!!\n' % (node, node.ref_count)) + + SCons.SConsign.Reset() + SCons.Script.Main.progress_display("scons: done clearing node information.") + + def do_clean(self, argv): + """\ + clean [TARGETS] Clean (remove) the specified TARGETS + and their dependencies. 'c' is a synonym. + """ + return self.do_build(['build', '--clean'] + argv[1:]) + + def do_EOF(self, argv): + print + self.do_exit(argv) + + def _do_one_help(self, arg): + try: + # If help_() exists, then call it. + func = getattr(self, 'help_' + arg) + except AttributeError: + try: + func = getattr(self, 'do_' + arg) + except AttributeError: + doc = None + else: + doc = self._doc_to_help(func) + if doc: + sys.stdout.write(doc + '\n') + sys.stdout.flush() + else: + doc = self.strip_initial_spaces(func()) + if doc: + sys.stdout.write(doc + '\n') + sys.stdout.flush() + + def _doc_to_help(self, obj): + doc = obj.__doc__ + if doc is None: + return '' + return self._strip_initial_spaces(doc) + + def _strip_initial_spaces(self, s): + #lines = s.split('\n') + lines = s.split('\n') + spaces = re.match(' *', lines[0]).group(0) + #def strip_spaces(l): + # if l.startswith(spaces): + # l = l[len(spaces):] + # return l + #return '\n'.join([ strip_spaces(l) for l in lines ]) + def strip_spaces(l, spaces=spaces): + if l[:len(spaces)] == spaces: + l = l[len(spaces):] + return l + lines = list(map(strip_spaces, lines)) + return '\n'.join(lines) + + def do_exit(self, argv): + """\ + exit Exit SCons interactive mode. + """ + sys.exit(0) + + def do_help(self, argv): + """\ + help [COMMAND] Prints help for the specified COMMAND. 'h' + and '?' are synonyms. + """ + if argv[1:]: + for arg in argv[1:]: + if self._do_one_help(arg): + break + else: + # If bare 'help' is called, print this class's doc + # string (if it has one). + doc = self._doc_to_help(self.__class__) + if doc: + sys.stdout.write(doc + '\n') + sys.stdout.flush() + + def do_shell(self, argv): + """\ + shell [COMMANDLINE] Execute COMMANDLINE in a subshell. 'sh' and + '!' are synonyms. + """ + import subprocess + argv = argv[1:] + if not argv: + argv = os.environ[self.shell_variable] + try: + # Per "[Python-Dev] subprocess insufficiently platform-independent?" + # http://mail.python.org/pipermail/python-dev/2008-August/081979.html "+ + # Doing the right thing with an argument list currently + # requires different shell= values on Windows and Linux. + p = subprocess.Popen(argv, shell=(sys.platform=='win32')) + except EnvironmentError, e: + sys.stderr.write('scons: %s: %s\n' % (argv[0], e.strerror)) + else: + p.wait() + + def do_version(self, argv): + """\ + version Prints SCons version information. + """ + sys.stdout.write(self.parser.version + '\n') + +def interact(fs, parser, options, targets, target_top): + c = SConsInteractiveCmd(prompt = 'scons>>> ', + fs = fs, + parser = parser, + options = options, + targets = targets, + target_top = target_top) + c.cmdloop() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Script/Main.py b/scons/scons-local-2.3.0/SCons/Script/Main.py new file mode 100644 index 000000000..3f1b407aa --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Script/Main.py @@ -0,0 +1,1407 @@ +"""SCons.Script + +This file implements the main() function used by the scons script. + +Architecturally, this *is* the scons script, and will likely only be +called from the external "scons" wrapper. Consequently, anything here +should not be, or be considered, part of the build engine. If it's +something that we expect other software to want to use, it should go in +some other module. If it's specific to the "scons" script invocation, +it goes here. +""" + +unsupported_python_version = (2, 3, 0) +deprecated_python_version = (2, 7, 0) + +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Script/Main.py 2013/03/03 09:48:35 garyo" + +import SCons.compat + +import os +import sys +import time +import traceback + +# Strip the script directory from sys.path() so on case-insensitive +# (Windows) systems Python doesn't think that the "scons" script is the +# "SCons" package. Replace it with our own version directory so, if +# if they're there, we pick up the right version of the build engine +# modules. +#sys.path = [os.path.join(sys.prefix, +# 'lib', +# 'scons-%d' % SCons.__version__)] + sys.path[1:] + +import SCons.CacheDir +import SCons.Debug +import SCons.Defaults +import SCons.Environment +import SCons.Errors +import SCons.Job +import SCons.Node +import SCons.Node.FS +import SCons.Platform +import SCons.SConf +import SCons.Script +import SCons.Taskmaster +import SCons.Util +import SCons.Warnings + +import SCons.Script.Interactive + +def fetch_win32_parallel_msg(): + # A subsidiary function that exists solely to isolate this import + # so we don't have to pull it in on all platforms, and so that an + # in-line "import" statement in the _main() function below doesn't + # cause warnings about local names shadowing use of the 'SCons' + # globl in nest scopes and UnboundLocalErrors and the like in some + # versions (2.1) of Python. + import SCons.Platform.win32 + return SCons.Platform.win32.parallel_msg + +# + +class SConsPrintHelpException(Exception): + pass + +display = SCons.Util.display +progress_display = SCons.Util.DisplayEngine() + +first_command_start = None +last_command_end = None + +class Progressor(object): + prev = '' + count = 0 + target_string = '$TARGET' + + def __init__(self, obj, interval=1, file=None, overwrite=False): + if file is None: + file = sys.stdout + + self.obj = obj + self.file = file + self.interval = interval + self.overwrite = overwrite + + if callable(obj): + self.func = obj + elif SCons.Util.is_List(obj): + self.func = self.spinner + elif obj.find(self.target_string) != -1: + self.func = self.replace_string + else: + self.func = self.string + + def write(self, s): + self.file.write(s) + self.file.flush() + self.prev = s + + def erase_previous(self): + if self.prev: + length = len(self.prev) + if self.prev[-1] in ('\n', '\r'): + length = length - 1 + self.write(' ' * length + '\r') + self.prev = '' + + def spinner(self, node): + self.write(self.obj[self.count % len(self.obj)]) + + def string(self, node): + self.write(self.obj) + + def replace_string(self, node): + self.write(self.obj.replace(self.target_string, str(node))) + + def __call__(self, node): + self.count = self.count + 1 + if (self.count % self.interval) == 0: + if self.overwrite: + self.erase_previous() + self.func(node) + +ProgressObject = SCons.Util.Null() + +def Progress(*args, **kw): + global ProgressObject + ProgressObject = Progressor(*args, **kw) + +# Task control. +# + +_BuildFailures = [] + +def GetBuildFailures(): + return _BuildFailures + +class BuildTask(SCons.Taskmaster.OutOfDateTask): + """An SCons build task.""" + progress = ProgressObject + + def display(self, message): + display('scons: ' + message) + + def prepare(self): + self.progress(self.targets[0]) + return SCons.Taskmaster.OutOfDateTask.prepare(self) + + def needs_execute(self): + if SCons.Taskmaster.OutOfDateTask.needs_execute(self): + return True + if self.top and self.targets[0].has_builder(): + display("scons: `%s' is up to date." % str(self.node)) + return False + + def execute(self): + if print_time: + start_time = time.time() + global first_command_start + if first_command_start is None: + first_command_start = start_time + SCons.Taskmaster.OutOfDateTask.execute(self) + if print_time: + global cumulative_command_time + global last_command_end + finish_time = time.time() + last_command_end = finish_time + cumulative_command_time = cumulative_command_time+finish_time-start_time + sys.stdout.write("Command execution time: %s: %f seconds\n"%(str(self.node), finish_time-start_time)) + + def do_failed(self, status=2): + _BuildFailures.append(self.exception[1]) + global exit_status + global this_build_status + if self.options.ignore_errors: + SCons.Taskmaster.OutOfDateTask.executed(self) + elif self.options.keep_going: + SCons.Taskmaster.OutOfDateTask.fail_continue(self) + exit_status = status + this_build_status = status + else: + SCons.Taskmaster.OutOfDateTask.fail_stop(self) + exit_status = status + this_build_status = status + + def executed(self): + t = self.targets[0] + if self.top and not t.has_builder() and not t.side_effect: + if not t.exists(): + if t.__class__.__name__ in ('File', 'Dir', 'Entry'): + errstr="Do not know how to make %s target `%s' (%s)." % (t.__class__.__name__, t, t.abspath) + else: # Alias or Python or ... + errstr="Do not know how to make %s target `%s'." % (t.__class__.__name__, t) + sys.stderr.write("scons: *** " + errstr) + if not self.options.keep_going: + sys.stderr.write(" Stop.") + sys.stderr.write("\n") + try: + raise SCons.Errors.BuildError(t, errstr) + except KeyboardInterrupt: + raise + except: + self.exception_set() + self.do_failed() + else: + print "scons: Nothing to be done for `%s'." % t + SCons.Taskmaster.OutOfDateTask.executed(self) + else: + SCons.Taskmaster.OutOfDateTask.executed(self) + + def failed(self): + # Handle the failure of a build task. The primary purpose here + # is to display the various types of Errors and Exceptions + # appropriately. + exc_info = self.exc_info() + try: + t, e, tb = exc_info + except ValueError: + t, e = exc_info + tb = None + + if t is None: + # The Taskmaster didn't record an exception for this Task; + # see if the sys module has one. + try: + t, e, tb = sys.exc_info()[:] + except ValueError: + t, e = exc_info + tb = None + + # Deprecated string exceptions will have their string stored + # in the first entry of the tuple. + if e is None: + e = t + + buildError = SCons.Errors.convert_to_BuildError(e) + if not buildError.node: + buildError.node = self.node + + node = buildError.node + if not SCons.Util.is_List(node): + node = [ node ] + nodename = ', '.join(map(str, node)) + + errfmt = "scons: *** [%s] %s\n" + sys.stderr.write(errfmt % (nodename, buildError)) + + if (buildError.exc_info[2] and buildError.exc_info[1] and + not isinstance( + buildError.exc_info[1], + (EnvironmentError, SCons.Errors.StopError, + SCons.Errors.UserError))): + type, value, trace = buildError.exc_info + traceback.print_exception(type, value, trace) + elif tb and print_stacktrace: + sys.stderr.write("scons: internal stack trace:\n") + traceback.print_tb(tb, file=sys.stderr) + + self.exception = (e, buildError, tb) # type, value, traceback + self.do_failed(buildError.exitstatus) + + self.exc_clear() + + def postprocess(self): + if self.top: + t = self.targets[0] + for tp in self.options.tree_printers: + tp.display(t) + if self.options.debug_includes: + tree = t.render_include_tree() + if tree: + print + print tree + SCons.Taskmaster.OutOfDateTask.postprocess(self) + + def make_ready(self): + """Make a task ready for execution""" + SCons.Taskmaster.OutOfDateTask.make_ready(self) + if self.out_of_date and self.options.debug_explain: + explanation = self.out_of_date[0].explain() + if explanation: + sys.stdout.write("scons: " + explanation) + +class CleanTask(SCons.Taskmaster.AlwaysTask): + """An SCons clean task.""" + def fs_delete(self, path, pathstr, remove=1): + try: + if os.path.lexists(path): + if os.path.isfile(path) or os.path.islink(path): + if remove: os.unlink(path) + display("Removed " + pathstr) + elif os.path.isdir(path) and not os.path.islink(path): + # delete everything in the dir + for e in sorted(os.listdir(path)): + p = os.path.join(path, e) + s = os.path.join(pathstr, e) + if os.path.isfile(p): + if remove: os.unlink(p) + display("Removed " + s) + else: + self.fs_delete(p, s, remove) + # then delete dir itself + if remove: os.rmdir(path) + display("Removed directory " + pathstr) + else: + errstr = "Path '%s' exists but isn't a file or directory." + raise SCons.Errors.UserError(errstr % (pathstr)) + except SCons.Errors.UserError, e: + print e + except (IOError, OSError), e: + print "scons: Could not remove '%s':" % pathstr, e.strerror + + def show(self): + target = self.targets[0] + if (target.has_builder() or target.side_effect) and not target.noclean: + for t in self.targets: + if not t.isdir(): + display("Removed " + str(t)) + if target in SCons.Environment.CleanTargets: + files = SCons.Environment.CleanTargets[target] + for f in files: + self.fs_delete(f.abspath, str(f), 0) + + def remove(self): + target = self.targets[0] + if (target.has_builder() or target.side_effect) and not target.noclean: + for t in self.targets: + try: + removed = t.remove() + except OSError, e: + # An OSError may indicate something like a permissions + # issue, an IOError would indicate something like + # the file not existing. In either case, print a + # message and keep going to try to remove as many + # targets aa possible. + print "scons: Could not remove '%s':" % str(t), e.strerror + else: + if removed: + display("Removed " + str(t)) + if target in SCons.Environment.CleanTargets: + files = SCons.Environment.CleanTargets[target] + for f in files: + self.fs_delete(f.abspath, str(f)) + + execute = remove + + # We want the Taskmaster to update the Node states (and therefore + # handle reference counts, etc.), but we don't want to call + # back to the Node's post-build methods, which would do things + # we don't want, like store .sconsign information. + executed = SCons.Taskmaster.Task.executed_without_callbacks + + # Have the taskmaster arrange to "execute" all of the targets, because + # we'll figure out ourselves (in remove() or show() above) whether + # anything really needs to be done. + make_ready = SCons.Taskmaster.Task.make_ready_all + + def prepare(self): + pass + +class QuestionTask(SCons.Taskmaster.AlwaysTask): + """An SCons task for the -q (question) option.""" + def prepare(self): + pass + + def execute(self): + if self.targets[0].get_state() != SCons.Node.up_to_date or \ + (self.top and not self.targets[0].exists()): + global exit_status + global this_build_status + exit_status = 1 + this_build_status = 1 + self.tm.stop() + + def executed(self): + pass + + +class TreePrinter(object): + def __init__(self, derived=False, prune=False, status=False): + self.derived = derived + self.prune = prune + self.status = status + def get_all_children(self, node): + return node.all_children() + def get_derived_children(self, node): + children = node.all_children(None) + return [x for x in children if x.has_builder()] + def display(self, t): + if self.derived: + func = self.get_derived_children + else: + func = self.get_all_children + s = self.status and 2 or 0 + SCons.Util.print_tree(t, func, prune=self.prune, showtags=s) + + +def python_version_string(): + return sys.version.split()[0] + +def python_version_unsupported(version=sys.version_info): + return version < unsupported_python_version + +def python_version_deprecated(version=sys.version_info): + return version < deprecated_python_version + + +# Global variables + +print_objects = 0 +print_memoizer = 0 +print_stacktrace = 0 +print_time = 0 +sconscript_time = 0 +cumulative_command_time = 0 +exit_status = 0 # final exit status, assume success by default +this_build_status = 0 # "exit status" of an individual build +num_jobs = None +delayed_warnings = [] + +class FakeOptionParser(object): + """ + A do-nothing option parser, used for the initial OptionsParser variable. + + During normal SCons operation, the OptionsParser is created right + away by the main() function. Certain tests scripts however, can + introspect on different Tool modules, the initialization of which + can try to add a new, local option to an otherwise uninitialized + OptionsParser object. This allows that introspection to happen + without blowing up. + + """ + class FakeOptionValues(object): + def __getattr__(self, attr): + return None + values = FakeOptionValues() + def add_local_option(self, *args, **kw): + pass + +OptionsParser = FakeOptionParser() + +def AddOption(*args, **kw): + if 'default' not in kw: + kw['default'] = None + result = OptionsParser.add_local_option(*args, **kw) + return result + +def GetOption(name): + return getattr(OptionsParser.values, name) + +def SetOption(name, value): + return OptionsParser.values.set_option(name, value) + +# +class Stats(object): + def __init__(self): + self.stats = [] + self.labels = [] + self.append = self.do_nothing + self.print_stats = self.do_nothing + def enable(self, outfp): + self.outfp = outfp + self.append = self.do_append + self.print_stats = self.do_print + def do_nothing(self, *args, **kw): + pass + +class CountStats(Stats): + def do_append(self, label): + self.labels.append(label) + self.stats.append(SCons.Debug.fetchLoggedInstances()) + def do_print(self): + stats_table = {} + for s in self.stats: + for n in [t[0] for t in s]: + stats_table[n] = [0, 0, 0, 0] + i = 0 + for s in self.stats: + for n, c in s: + stats_table[n][i] = c + i = i + 1 + self.outfp.write("Object counts:\n") + pre = [" "] + post = [" %s\n"] + l = len(self.stats) + fmt1 = ''.join(pre + [' %7s']*l + post) + fmt2 = ''.join(pre + [' %7d']*l + post) + labels = self.labels[:l] + labels.append(("", "Class")) + self.outfp.write(fmt1 % tuple([x[0] for x in labels])) + self.outfp.write(fmt1 % tuple([x[1] for x in labels])) + for k in sorted(stats_table.keys()): + r = stats_table[k][:l] + [k] + self.outfp.write(fmt2 % tuple(r)) + +count_stats = CountStats() + +class MemStats(Stats): + def do_append(self, label): + self.labels.append(label) + self.stats.append(SCons.Debug.memory()) + def do_print(self): + fmt = 'Memory %-32s %12d\n' + for label, stats in zip(self.labels, self.stats): + self.outfp.write(fmt % (label, stats)) + +memory_stats = MemStats() + +# utility functions + +def _scons_syntax_error(e): + """Handle syntax errors. Print out a message and show where the error + occurred. + """ + etype, value, tb = sys.exc_info() + lines = traceback.format_exception_only(etype, value) + for line in lines: + sys.stderr.write(line+'\n') + sys.exit(2) + +def find_deepest_user_frame(tb): + """ + Find the deepest stack frame that is not part of SCons. + + Input is a "pre-processed" stack trace in the form + returned by traceback.extract_tb() or traceback.extract_stack() + """ + + tb.reverse() + + # find the deepest traceback frame that is not part + # of SCons: + for frame in tb: + filename = frame[0] + if filename.find(os.sep+'SCons'+os.sep) == -1: + return frame + return tb[0] + +def _scons_user_error(e): + """Handle user errors. Print out a message and a description of the + error, along with the line number and routine where it occured. + The file and line number will be the deepest stack frame that is + not part of SCons itself. + """ + global print_stacktrace + etype, value, tb = sys.exc_info() + if print_stacktrace: + traceback.print_exception(etype, value, tb) + filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb)) + sys.stderr.write("\nscons: *** %s\n" % value) + sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine)) + sys.exit(2) + +def _scons_user_warning(e): + """Handle user warnings. Print out a message and a description of + the warning, along with the line number and routine where it occured. + The file and line number will be the deepest stack frame that is + not part of SCons itself. + """ + etype, value, tb = sys.exc_info() + filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb)) + sys.stderr.write("\nscons: warning: %s\n" % e) + sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine)) + +def _scons_internal_warning(e): + """Slightly different from _scons_user_warning in that we use the + *current call stack* rather than sys.exc_info() to get our stack trace. + This is used by the warnings framework to print warnings.""" + filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_stack()) + sys.stderr.write("\nscons: warning: %s\n" % e.args[0]) + sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine)) + +def _scons_internal_error(): + """Handle all errors but user errors. Print out a message telling + the user what to do in this case and print a normal trace. + """ + print 'internal error' + traceback.print_exc() + sys.exit(2) + +def _SConstruct_exists(dirname='', repositories=[], filelist=None): + """This function checks that an SConstruct file exists in a directory. + If so, it returns the path of the file. By default, it checks the + current directory. + """ + if not filelist: + filelist = ['SConstruct', 'Sconstruct', 'sconstruct'] + for file in filelist: + sfile = os.path.join(dirname, file) + if os.path.isfile(sfile): + return sfile + if not os.path.isabs(sfile): + for rep in repositories: + if os.path.isfile(os.path.join(rep, sfile)): + return sfile + return None + +def _set_debug_values(options): + global print_memoizer, print_objects, print_stacktrace, print_time + + debug_values = options.debug + + if "count" in debug_values: + # All of the object counts are within "if __debug__:" blocks, + # which get stripped when running optimized (with python -O or + # from compiled *.pyo files). Provide a warning if __debug__ is + # stripped, so it doesn't just look like --debug=count is broken. + enable_count = False + if __debug__: enable_count = True + if enable_count: + count_stats.enable(sys.stdout) + else: + msg = "--debug=count is not supported when running SCons\n" + \ + "\twith the python -O option or optimized (.pyo) modules." + SCons.Warnings.warn(SCons.Warnings.NoObjectCountWarning, msg) + if "dtree" in debug_values: + options.tree_printers.append(TreePrinter(derived=True)) + options.debug_explain = ("explain" in debug_values) + if "findlibs" in debug_values: + SCons.Scanner.Prog.print_find_libs = "findlibs" + options.debug_includes = ("includes" in debug_values) + print_memoizer = ("memoizer" in debug_values) + if "memory" in debug_values: + memory_stats.enable(sys.stdout) + print_objects = ("objects" in debug_values) + if "presub" in debug_values: + SCons.Action.print_actions_presub = 1 + if "stacktrace" in debug_values: + print_stacktrace = 1 + if "stree" in debug_values: + options.tree_printers.append(TreePrinter(status=True)) + if "time" in debug_values: + print_time = 1 + if "tree" in debug_values: + options.tree_printers.append(TreePrinter()) + if "prepare" in debug_values: + SCons.Taskmaster.print_prepare = 1 + if "duplicate" in debug_values: + SCons.Node.FS.print_duplicate = 1 + +def _create_path(plist): + path = '.' + for d in plist: + if os.path.isabs(d): + path = d + else: + path = path + '/' + d + return path + +def _load_site_scons_dir(topdir, site_dir_name=None): + """Load the site_scons dir under topdir. + Prepends site_scons to sys.path, imports site_scons/site_init.py, + and prepends site_scons/site_tools to default toolpath.""" + if site_dir_name: + err_if_not_found = True # user specified: err if missing + else: + site_dir_name = "site_scons" + err_if_not_found = False + + site_dir = os.path.join(topdir, site_dir_name) + if not os.path.exists(site_dir): + if err_if_not_found: + raise SCons.Errors.UserError("site dir %s not found."%site_dir) + return + + site_init_filename = "site_init.py" + site_init_modname = "site_init" + site_tools_dirname = "site_tools" + # prepend to sys.path + sys.path = [os.path.abspath(site_dir)] + sys.path + site_init_file = os.path.join(site_dir, site_init_filename) + site_tools_dir = os.path.join(site_dir, site_tools_dirname) + if os.path.exists(site_init_file): + import imp, re + # TODO(2.4): turn this into try:-except:-finally: + try: + try: + fp, pathname, description = imp.find_module(site_init_modname, + [site_dir]) + # Load the file into SCons.Script namespace. This is + # opaque and clever; m is the module object for the + # SCons.Script module, and the exec ... in call executes a + # file (or string containing code) in the context of the + # module's dictionary, so anything that code defines ends + # up adding to that module. This is really short, but all + # the error checking makes it longer. + try: + m = sys.modules['SCons.Script'] + except Exception, e: + fmt = 'cannot import site_init.py: missing SCons.Script module %s' + raise SCons.Errors.InternalError(fmt % repr(e)) + try: + sfx = description[0] + modname = os.path.basename(pathname)[:-len(sfx)] + site_m = {"__file__": pathname, "__name__": modname, "__doc__": None} + re_special = re.compile("__[^_]+__") + for k in m.__dict__.keys(): + if not re_special.match(k): + site_m[k] = m.__dict__[k] + + # This is the magic. + exec fp in site_m + except KeyboardInterrupt: + raise + except Exception, e: + fmt = '*** Error loading site_init file %s:\n' + sys.stderr.write(fmt % repr(site_init_file)) + raise + else: + for k in site_m: + if not re_special.match(k): + m.__dict__[k] = site_m[k] + except KeyboardInterrupt: + raise + except ImportError, e: + fmt = '*** cannot import site init file %s:\n' + sys.stderr.write(fmt % repr(site_init_file)) + raise + finally: + if fp: + fp.close() + if os.path.exists(site_tools_dir): + # prepend to DefaultToolpath + SCons.Tool.DefaultToolpath.insert(0, os.path.abspath(site_tools_dir)) + +def _load_all_site_scons_dirs(topdir, verbose=None): + """Load all of the predefined site_scons dir. + Order is significant; we load them in order from most generic + (machine-wide) to most specific (topdir). + The verbose argument is only for testing. + """ + platform = SCons.Platform.platform_default() + + def homedir(d): + return os.path.expanduser('~/'+d) + + if platform == 'win32' or platform == 'cygwin': + # Note we use $ here instead of %...% because older + # pythons (prior to 2.6?) didn't expand %...% on Windows. + # This set of dirs should work on XP, Vista, 7 and later. + sysdirs=[ + os.path.expandvars('$ALLUSERSPROFILE\\Application Data\\scons'), + os.path.expandvars('$USERPROFILE\\Local Settings\\Application Data\\scons')] + appdatadir = os.path.expandvars('$APPDATA\\scons') + if appdatadir not in sysdirs: + sysdirs.append(appdatadir) + sysdirs.append(homedir('.scons')) + + elif platform == 'darwin': # MacOS X + sysdirs=['/Library/Application Support/SCons', + '/opt/local/share/scons', # (for MacPorts) + '/sw/share/scons', # (for Fink) + homedir('Library/Application Support/SCons'), + homedir('.scons')] + elif platform == 'sunos': # Solaris + sysdirs=['/opt/sfw/scons', + '/usr/share/scons', + homedir('.scons')] + else: # Linux, HPUX, etc. + # assume posix-like, i.e. platform == 'posix' + sysdirs=['/usr/share/scons', + homedir('.scons')] + + dirs=sysdirs + [topdir] + for d in dirs: + if verbose: # this is used by unit tests. + print "Loading site dir ", d + _load_site_scons_dir(d) + +def test_load_all_site_scons_dirs(d): + _load_all_site_scons_dirs(d, True) + +def version_string(label, module): + version = module.__version__ + build = module.__build__ + if build: + if build[0] != '.': + build = '.' + build + version = version + build + fmt = "\t%s: v%s, %s, by %s on %s\n" + return fmt % (label, + version, + module.__date__, + module.__developer__, + module.__buildsys__) + +def path_string(label, module): + path = module.__path__ + return "\t%s path: %s\n"%(label,path) + +def _main(parser): + global exit_status + global this_build_status + + options = parser.values + + # Here's where everything really happens. + + # First order of business: set up default warnings and then + # handle the user's warning options, so that we can issue (or + # suppress) appropriate warnings about anything that might happen, + # as configured by the user. + + default_warnings = [ SCons.Warnings.WarningOnByDefault, + SCons.Warnings.DeprecatedWarning, + ] + + for warning in default_warnings: + SCons.Warnings.enableWarningClass(warning) + SCons.Warnings._warningOut = _scons_internal_warning + SCons.Warnings.process_warn_strings(options.warn) + + # Now that we have the warnings configuration set up, we can actually + # issue (or suppress) any warnings about warning-worthy things that + # occurred while the command-line options were getting parsed. + try: + dw = options.delayed_warnings + except AttributeError: + pass + else: + delayed_warnings.extend(dw) + for warning_type, message in delayed_warnings: + SCons.Warnings.warn(warning_type, message) + + if options.diskcheck: + SCons.Node.FS.set_diskcheck(options.diskcheck) + + # Next, we want to create the FS object that represents the outside + # world's file system, as that's central to a lot of initialization. + # To do this, however, we need to be in the directory from which we + # want to start everything, which means first handling any relevant + # options that might cause us to chdir somewhere (-C, -D, -U, -u). + if options.directory: + script_dir = os.path.abspath(_create_path(options.directory)) + else: + script_dir = os.getcwd() + + target_top = None + if options.climb_up: + target_top = '.' # directory to prepend to targets + while script_dir and not _SConstruct_exists(script_dir, + options.repository, + options.file): + script_dir, last_part = os.path.split(script_dir) + if last_part: + target_top = os.path.join(last_part, target_top) + else: + script_dir = '' + + if script_dir and script_dir != os.getcwd(): + if not options.silent: + display("scons: Entering directory `%s'" % script_dir) + try: + os.chdir(script_dir) + except OSError: + sys.stderr.write("Could not change directory to %s\n" % script_dir) + + # Now that we're in the top-level SConstruct directory, go ahead + # and initialize the FS object that represents the file system, + # and make it the build engine default. + fs = SCons.Node.FS.get_default_fs() + + for rep in options.repository: + fs.Repository(rep) + + # Now that we have the FS object, the next order of business is to + # check for an SConstruct file (or other specified config file). + # If there isn't one, we can bail before doing any more work. + scripts = [] + if options.file: + scripts.extend(options.file) + if not scripts: + sfile = _SConstruct_exists(repositories=options.repository, + filelist=options.file) + if sfile: + scripts.append(sfile) + + if not scripts: + if options.help: + # There's no SConstruct, but they specified -h. + # Give them the options usage now, before we fail + # trying to read a non-existent SConstruct file. + raise SConsPrintHelpException + raise SCons.Errors.UserError("No SConstruct file found.") + + if scripts[0] == "-": + d = fs.getcwd() + else: + d = fs.File(scripts[0]).dir + fs.set_SConstruct_dir(d) + + _set_debug_values(options) + SCons.Node.implicit_cache = options.implicit_cache + SCons.Node.implicit_deps_changed = options.implicit_deps_changed + SCons.Node.implicit_deps_unchanged = options.implicit_deps_unchanged + + if options.no_exec: + SCons.SConf.dryrun = 1 + SCons.Action.execute_actions = None + if options.question: + SCons.SConf.dryrun = 1 + if options.clean: + SCons.SConf.SetBuildType('clean') + if options.help: + SCons.SConf.SetBuildType('help') + SCons.SConf.SetCacheMode(options.config) + SCons.SConf.SetProgressDisplay(progress_display) + + if options.no_progress or options.silent: + progress_display.set_mode(0) + + if options.site_dir: + _load_site_scons_dir(d.path, options.site_dir) + elif not options.no_site_dir: + _load_all_site_scons_dirs(d.path) + + if options.include_dir: + sys.path = options.include_dir + sys.path + + # That should cover (most of) the options. Next, set up the variables + # that hold command-line arguments, so the SConscript files that we + # read and execute have access to them. + targets = [] + xmit_args = [] + for a in parser.largs: + if a[:1] == '-': + continue + if '=' in a: + xmit_args.append(a) + else: + targets.append(a) + SCons.Script._Add_Targets(targets + parser.rargs) + SCons.Script._Add_Arguments(xmit_args) + + # If stdout is not a tty, replace it with a wrapper object to call flush + # after every write. + # + # Tty devices automatically flush after every newline, so the replacement + # isn't necessary. Furthermore, if we replace sys.stdout, the readline + # module will no longer work. This affects the behavior during + # --interactive mode. --interactive should only be used when stdin and + # stdout refer to a tty. + if not hasattr(sys.stdout, 'isatty') or not sys.stdout.isatty(): + sys.stdout = SCons.Util.Unbuffered(sys.stdout) + if not hasattr(sys.stderr, 'isatty') or not sys.stderr.isatty(): + sys.stderr = SCons.Util.Unbuffered(sys.stderr) + + memory_stats.append('before reading SConscript files:') + count_stats.append(('pre-', 'read')) + + # And here's where we (finally) read the SConscript files. + + progress_display("scons: Reading SConscript files ...") + + start_time = time.time() + try: + for script in scripts: + SCons.Script._SConscript._SConscript(fs, script) + except SCons.Errors.StopError, e: + # We had problems reading an SConscript file, such as it + # couldn't be copied in to the VariantDir. Since we're just + # reading SConscript files and haven't started building + # things yet, stop regardless of whether they used -i or -k + # or anything else. + sys.stderr.write("scons: *** %s Stop.\n" % e) + exit_status = 2 + sys.exit(exit_status) + global sconscript_time + sconscript_time = time.time() - start_time + + progress_display("scons: done reading SConscript files.") + + memory_stats.append('after reading SConscript files:') + count_stats.append(('post-', 'read')) + + # Re-{enable,disable} warnings in case they disabled some in + # the SConscript file. + # + # We delay enabling the PythonVersionWarning class until here so that, + # if they explicity disabled it in either in the command line or in + # $SCONSFLAGS, or in the SConscript file, then the search through + # the list of deprecated warning classes will find that disabling + # first and not issue the warning. + #SCons.Warnings.enableWarningClass(SCons.Warnings.PythonVersionWarning) + SCons.Warnings.process_warn_strings(options.warn) + + # Now that we've read the SConscript files, we can check for the + # warning about deprecated Python versions--delayed until here + # in case they disabled the warning in the SConscript files. + if python_version_deprecated(): + msg = "Support for pre-%s Python version (%s) is deprecated.\n" + \ + " If this will cause hardship, contact dev@scons.tigris.org." + deprecated_version_string = ".".join(map(str, deprecated_python_version)) + SCons.Warnings.warn(SCons.Warnings.PythonVersionWarning, + msg % (deprecated_version_string, python_version_string())) + + if not options.help: + SCons.SConf.CreateConfigHBuilder(SCons.Defaults.DefaultEnvironment()) + + # Now re-parse the command-line options (any to the left of a '--' + # argument, that is) with any user-defined command-line options that + # the SConscript files may have added to the parser object. This will + # emit the appropriate error message and exit if any unknown option + # was specified on the command line. + + parser.preserve_unknown_options = False + parser.parse_args(parser.largs, options) + + if options.help: + help_text = SCons.Script.help_text + if help_text is None: + # They specified -h, but there was no Help() inside the + # SConscript files. Give them the options usage. + raise SConsPrintHelpException + else: + print help_text + print "Use scons -H for help about command-line options." + exit_status = 0 + return + + # Change directory to the top-level SConstruct directory, then tell + # the Node.FS subsystem that we're all done reading the SConscript + # files and calling Repository() and VariantDir() and changing + # directories and the like, so it can go ahead and start memoizing + # the string values of file system nodes. + + fs.chdir(fs.Top) + + SCons.Node.FS.save_strings(1) + + # Now that we've read the SConscripts we can set the options + # that are SConscript settable: + SCons.Node.implicit_cache = options.implicit_cache + SCons.Node.FS.set_duplicate(options.duplicate) + fs.set_max_drift(options.max_drift) + + SCons.Job.explicit_stack_size = options.stack_size + + if options.md5_chunksize: + SCons.Node.FS.File.md5_chunksize = options.md5_chunksize + + platform = SCons.Platform.platform_module() + + if options.interactive: + SCons.Script.Interactive.interact(fs, OptionsParser, options, + targets, target_top) + + else: + + # Build the targets + nodes = _build_targets(fs, options, targets, target_top) + if not nodes: + exit_status = 2 + +def _build_targets(fs, options, targets, target_top): + + global this_build_status + this_build_status = 0 + + progress_display.set_mode(not (options.no_progress or options.silent)) + display.set_mode(not options.silent) + SCons.Action.print_actions = not options.silent + SCons.Action.execute_actions = not options.no_exec + SCons.Node.FS.do_store_info = not options.no_exec + SCons.SConf.dryrun = options.no_exec + + if options.diskcheck: + SCons.Node.FS.set_diskcheck(options.diskcheck) + + SCons.CacheDir.cache_enabled = not options.cache_disable + SCons.CacheDir.cache_debug = options.cache_debug + SCons.CacheDir.cache_force = options.cache_force + SCons.CacheDir.cache_show = options.cache_show + + if options.no_exec: + CleanTask.execute = CleanTask.show + else: + CleanTask.execute = CleanTask.remove + + lookup_top = None + if targets or SCons.Script.BUILD_TARGETS != SCons.Script._build_plus_default: + # They specified targets on the command line or modified + # BUILD_TARGETS in the SConscript file(s), so if they used -u, + # -U or -D, we have to look up targets relative to the top, + # but we build whatever they specified. + if target_top: + lookup_top = fs.Dir(target_top) + target_top = None + + targets = SCons.Script.BUILD_TARGETS + else: + # There are no targets specified on the command line, + # so if they used -u, -U or -D, we may have to restrict + # what actually gets built. + d = None + if target_top: + if options.climb_up == 1: + # -u, local directory and below + target_top = fs.Dir(target_top) + lookup_top = target_top + elif options.climb_up == 2: + # -D, all Default() targets + target_top = None + lookup_top = None + elif options.climb_up == 3: + # -U, local SConscript Default() targets + target_top = fs.Dir(target_top) + def check_dir(x, target_top=target_top): + if hasattr(x, 'cwd') and not x.cwd is None: + cwd = x.cwd.srcnode() + return cwd == target_top + else: + # x doesn't have a cwd, so it's either not a target, + # or not a file, so go ahead and keep it as a default + # target and let the engine sort it out: + return 1 + d = list(filter(check_dir, SCons.Script.DEFAULT_TARGETS)) + SCons.Script.DEFAULT_TARGETS[:] = d + target_top = None + lookup_top = None + + targets = SCons.Script._Get_Default_Targets(d, fs) + + if not targets: + sys.stderr.write("scons: *** No targets specified and no Default() targets found. Stop.\n") + return None + + def Entry(x, ltop=lookup_top, ttop=target_top, fs=fs): + if isinstance(x, SCons.Node.Node): + node = x + else: + node = None + # Why would ltop be None? Unfortunately this happens. + if ltop is None: ltop = '' + # Curdir becomes important when SCons is called with -u, -C, + # or similar option that changes directory, and so the paths + # of targets given on the command line need to be adjusted. + curdir = os.path.join(os.getcwd(), str(ltop)) + for lookup in SCons.Node.arg2nodes_lookups: + node = lookup(x, curdir=curdir) + if node is not None: + break + if node is None: + node = fs.Entry(x, directory=ltop, create=1) + if ttop and not node.is_under(ttop): + if isinstance(node, SCons.Node.FS.Dir) and ttop.is_under(node): + node = ttop + else: + node = None + return node + + nodes = [_f for _f in map(Entry, targets) if _f] + + task_class = BuildTask # default action is to build targets + opening_message = "Building targets ..." + closing_message = "done building targets." + if options.keep_going: + failure_message = "done building targets (errors occurred during build)." + else: + failure_message = "building terminated because of errors." + if options.question: + task_class = QuestionTask + try: + if options.clean: + task_class = CleanTask + opening_message = "Cleaning targets ..." + closing_message = "done cleaning targets." + if options.keep_going: + failure_message = "done cleaning targets (errors occurred during clean)." + else: + failure_message = "cleaning terminated because of errors." + except AttributeError: + pass + + task_class.progress = ProgressObject + + if options.random: + def order(dependencies): + """Randomize the dependencies.""" + import random + # This is cribbed from the implementation of + # random.shuffle() in Python 2.X. + d = dependencies + for i in range(len(d)-1, 0, -1): + j = int(random.random() * (i+1)) + d[i], d[j] = d[j], d[i] + return d + else: + def order(dependencies): + """Leave the order of dependencies alone.""" + return dependencies + + if options.taskmastertrace_file == '-': + tmtrace = sys.stdout + elif options.taskmastertrace_file: + tmtrace = open(options.taskmastertrace_file, 'wb') + else: + tmtrace = None + taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, order, tmtrace) + + # Let the BuildTask objects get at the options to respond to the + # various print_* settings, tree_printer list, etc. + BuildTask.options = options + + global num_jobs + num_jobs = options.num_jobs + jobs = SCons.Job.Jobs(num_jobs, taskmaster) + if num_jobs > 1: + msg = None + if jobs.num_jobs == 1: + msg = "parallel builds are unsupported by this version of Python;\n" + \ + "\tignoring -j or num_jobs option.\n" + elif sys.platform == 'win32': + msg = fetch_win32_parallel_msg() + if msg: + SCons.Warnings.warn(SCons.Warnings.NoParallelSupportWarning, msg) + + memory_stats.append('before building targets:') + count_stats.append(('pre-', 'build')) + + def jobs_postfunc( + jobs=jobs, + options=options, + closing_message=closing_message, + failure_message=failure_message + ): + if jobs.were_interrupted(): + if not options.no_progress and not options.silent: + sys.stderr.write("scons: Build interrupted.\n") + global exit_status + global this_build_status + exit_status = 2 + this_build_status = 2 + + if this_build_status: + progress_display("scons: " + failure_message) + else: + progress_display("scons: " + closing_message) + if not options.no_exec: + if jobs.were_interrupted(): + progress_display("scons: writing .sconsign file.") + SCons.SConsign.write() + + progress_display("scons: " + opening_message) + jobs.run(postfunc = jobs_postfunc) + + memory_stats.append('after building targets:') + count_stats.append(('post-', 'build')) + + return nodes + +def _exec_main(parser, values): + sconsflags = os.environ.get('SCONSFLAGS', '') + all_args = sconsflags.split() + sys.argv[1:] + + options, args = parser.parse_args(all_args, values) + + if isinstance(options.debug, list) and "pdb" in options.debug: + import pdb + pdb.Pdb().runcall(_main, parser) + elif options.profile_file: + # compat layer imports "cProfile" for us if it's available. + from profile import Profile + + # Some versions of Python 2.4 shipped a profiler that had the + # wrong 'c_exception' entry in its dispatch table. Make sure + # we have the right one. (This may put an unnecessary entry + # in the table in earlier versions of Python, but its presence + # shouldn't hurt anything). + try: + dispatch = Profile.dispatch + except AttributeError: + pass + else: + dispatch['c_exception'] = Profile.trace_dispatch_return + + prof = Profile() + try: + prof.runcall(_main, parser) + except SConsPrintHelpException, e: + prof.dump_stats(options.profile_file) + raise e + except SystemExit: + pass + prof.dump_stats(options.profile_file) + else: + _main(parser) + +def main(): + global OptionsParser + global exit_status + global first_command_start + + # Check up front for a Python version we do not support. We + # delay the check for deprecated Python versions until later, + # after the SConscript files have been read, in case they + # disable that warning. + if python_version_unsupported(): + msg = "scons: *** SCons version %s does not run under Python version %s.\n" + sys.stderr.write(msg % (SCons.__version__, python_version_string())) + sys.exit(1) + + parts = ["SCons by Steven Knight et al.:\n"] + try: + import __main__ + parts.append(version_string("script", __main__)) + except (ImportError, AttributeError): + # On Windows there is no scons.py, so there is no + # __main__.__version__, hence there is no script version. + pass + parts.append(version_string("engine", SCons)) + parts.append(path_string("engine", SCons)) + parts.append("Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation") + version = ''.join(parts) + + import SConsOptions + parser = SConsOptions.Parser(version) + values = SConsOptions.SConsValues(parser.get_default_values()) + + OptionsParser = parser + + try: + _exec_main(parser, values) + except SystemExit, s: + if s: + exit_status = s + except KeyboardInterrupt: + print("scons: Build interrupted.") + sys.exit(2) + except SyntaxError, e: + _scons_syntax_error(e) + except SCons.Errors.InternalError: + _scons_internal_error() + except SCons.Errors.UserError, e: + _scons_user_error(e) + except SConsPrintHelpException: + parser.print_help() + exit_status = 0 + except SCons.Errors.BuildError, e: + exit_status = e.exitstatus + except: + # An exception here is likely a builtin Python exception Python + # code in an SConscript file. Show them precisely what the + # problem was and where it happened. + SCons.Script._SConscript.SConscript_exception() + sys.exit(2) + + memory_stats.print_stats() + count_stats.print_stats() + + if print_objects: + SCons.Debug.listLoggedInstances('*') + #SCons.Debug.dumpLoggedInstances('*') + + if print_memoizer: + SCons.Memoize.Dump("Memoizer (memory cache) hits and misses:") + + # Dump any development debug info that may have been enabled. + # These are purely for internal debugging during development, so + # there's no need to control them with --debug= options; they're + # controlled by changing the source code. + SCons.Debug.dump_caller_counts() + SCons.Taskmaster.dump_stats() + + if print_time: + total_time = time.time() - SCons.Script.start_time + if num_jobs == 1: + ct = cumulative_command_time + else: + if last_command_end is None or first_command_start is None: + ct = 0.0 + else: + ct = last_command_end - first_command_start + scons_time = total_time - sconscript_time - ct + print "Total build time: %f seconds"%total_time + print "Total SConscript file execution time: %f seconds"%sconscript_time + print "Total SCons execution time: %f seconds"%scons_time + print "Total command execution time: %f seconds"%ct + + sys.exit(exit_status) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Script/SConsOptions.py b/scons/scons-local-2.3.0/SCons/Script/SConsOptions.py new file mode 100644 index 000000000..6a6bae3e7 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Script/SConsOptions.py @@ -0,0 +1,946 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Script/SConsOptions.py 2013/03/03 09:48:35 garyo" + +import optparse +import re +import sys +import textwrap + +no_hyphen_re = re.compile(r'(\s+|(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))') + +try: + from gettext import gettext +except ImportError: + def gettext(message): + return message +_ = gettext + +import SCons.Node.FS +import SCons.Warnings + +OptionValueError = optparse.OptionValueError +SUPPRESS_HELP = optparse.SUPPRESS_HELP + +diskcheck_all = SCons.Node.FS.diskcheck_types() + +def diskcheck_convert(value): + if value is None: + return [] + if not SCons.Util.is_List(value): + value = value.split(',') + result = [] + for v in value: + v = v.lower() + if v == 'all': + result = diskcheck_all + elif v == 'none': + result = [] + elif v in diskcheck_all: + result.append(v) + else: + raise ValueError(v) + return result + +class SConsValues(optparse.Values): + """ + Holder class for uniform access to SCons options, regardless + of whether or not they can be set on the command line or in the + SConscript files (using the SetOption() function). + + A SCons option value can originate three different ways: + + 1) set on the command line; + 2) set in an SConscript file; + 3) the default setting (from the the op.add_option() + calls in the Parser() function, below). + + The command line always overrides a value set in a SConscript file, + which in turn always overrides default settings. Because we want + to support user-specified options in the SConscript file itself, + though, we may not know about all of the options when the command + line is first parsed, so we can't make all the necessary precedence + decisions at the time the option is configured. + + The solution implemented in this class is to keep these different sets + of settings separate (command line, SConscript file, and default) + and to override the __getattr__() method to check them in turn. + This should allow the rest of the code to just fetch values as + attributes of an instance of this class, without having to worry + about where they came from. + + Note that not all command line options are settable from SConscript + files, and the ones that are must be explicitly added to the + "settable" list in this class, and optionally validated and coerced + in the set_option() method. + """ + + def __init__(self, defaults): + self.__dict__['__defaults__'] = defaults + self.__dict__['__SConscript_settings__'] = {} + + def __getattr__(self, attr): + """ + Fetches an options value, checking first for explicit settings + from the command line (which are direct attributes), then the + SConscript file settings, then the default values. + """ + try: + return self.__dict__[attr] + except KeyError: + try: + return self.__dict__['__SConscript_settings__'][attr] + except KeyError: + return getattr(self.__dict__['__defaults__'], attr) + + settable = [ + 'clean', + 'diskcheck', + 'duplicate', + 'help', + 'implicit_cache', + 'max_drift', + 'md5_chunksize', + 'no_exec', + 'num_jobs', + 'random', + 'stack_size', + 'warn', + ] + + def set_option(self, name, value): + """ + Sets an option from an SConscript file. + """ + if not name in self.settable: + raise SCons.Errors.UserError("This option is not settable from a SConscript file: %s"%name) + + if name == 'num_jobs': + try: + value = int(value) + if value < 1: + raise ValueError + except ValueError: + raise SCons.Errors.UserError("A positive integer is required: %s"%repr(value)) + elif name == 'max_drift': + try: + value = int(value) + except ValueError: + raise SCons.Errors.UserError("An integer is required: %s"%repr(value)) + elif name == 'duplicate': + try: + value = str(value) + except ValueError: + raise SCons.Errors.UserError("A string is required: %s"%repr(value)) + if not value in SCons.Node.FS.Valid_Duplicates: + raise SCons.Errors.UserError("Not a valid duplication style: %s" % value) + # Set the duplicate style right away so it can affect linking + # of SConscript files. + SCons.Node.FS.set_duplicate(value) + elif name == 'diskcheck': + try: + value = diskcheck_convert(value) + except ValueError, v: + raise SCons.Errors.UserError("Not a valid diskcheck value: %s"%v) + if 'diskcheck' not in self.__dict__: + # No --diskcheck= option was specified on the command line. + # Set this right away so it can affect the rest of the + # file/Node lookups while processing the SConscript files. + SCons.Node.FS.set_diskcheck(value) + elif name == 'stack_size': + try: + value = int(value) + except ValueError: + raise SCons.Errors.UserError("An integer is required: %s"%repr(value)) + elif name == 'md5_chunksize': + try: + value = int(value) + except ValueError: + raise SCons.Errors.UserError("An integer is required: %s"%repr(value)) + elif name == 'warn': + if SCons.Util.is_String(value): + value = [value] + value = self.__SConscript_settings__.get(name, []) + value + SCons.Warnings.process_warn_strings(value) + + self.__SConscript_settings__[name] = value + +class SConsOption(optparse.Option): + def convert_value(self, opt, value): + if value is not None: + if self.nargs in (1, '?'): + return self.check_value(opt, value) + else: + return tuple([self.check_value(opt, v) for v in value]) + + def process(self, opt, value, values, parser): + + # First, convert the value(s) to the right type. Howl if any + # value(s) are bogus. + value = self.convert_value(opt, value) + + # And then take whatever action is expected of us. + # This is a separate method to make life easier for + # subclasses to add new actions. + return self.take_action( + self.action, self.dest, opt, value, values, parser) + + def _check_nargs_optional(self): + if self.nargs == '?' and self._short_opts: + fmt = "option %s: nargs='?' is incompatible with short options" + raise SCons.Errors.UserError(fmt % self._short_opts[0]) + + try: + _orig_CONST_ACTIONS = optparse.Option.CONST_ACTIONS + + _orig_CHECK_METHODS = optparse.Option.CHECK_METHODS + + except AttributeError: + # optparse.Option had no CONST_ACTIONS before Python 2.5. + + _orig_CONST_ACTIONS = ("store_const",) + + def _check_const(self): + if self.action not in self.CONST_ACTIONS and self.const is not None: + raise OptionError( + "'const' must not be supplied for action %r" % self.action, + self) + + # optparse.Option collects its list of unbound check functions + # up front. This sucks because it means we can't just override + # the _check_const() function like a normal method, we have to + # actually replace it in the list. This seems to be the most + # straightforward way to do that. + + _orig_CHECK_METHODS = [optparse.Option._check_action, + optparse.Option._check_type, + optparse.Option._check_choice, + optparse.Option._check_dest, + _check_const, + optparse.Option._check_nargs, + optparse.Option._check_callback] + + CHECK_METHODS = _orig_CHECK_METHODS + [_check_nargs_optional] + + CONST_ACTIONS = _orig_CONST_ACTIONS + optparse.Option.TYPED_ACTIONS + +class SConsOptionGroup(optparse.OptionGroup): + """ + A subclass for SCons-specific option groups. + + The only difference between this and the base class is that we print + the group's help text flush left, underneath their own title but + lined up with the normal "SCons Options". + """ + def format_help(self, formatter): + """ + Format an option group's help text, outdenting the title so it's + flush with the "SCons Options" title we print at the top. + """ + formatter.dedent() + result = formatter.format_heading(self.title) + formatter.indent() + result = result + optparse.OptionContainer.format_help(self, formatter) + return result + +class SConsOptionParser(optparse.OptionParser): + preserve_unknown_options = False + + def error(self, msg): + # overriden OptionValueError exception handler + self.print_usage(sys.stderr) + sys.stderr.write("SCons Error: %s\n" % msg) + sys.exit(2) + + def _process_long_opt(self, rargs, values): + """ + SCons-specific processing of long options. + + This is copied directly from the normal + optparse._process_long_opt() method, except that, if configured + to do so, we catch the exception thrown when an unknown option + is encountered and just stick it back on the "leftover" arguments + for later (re-)processing. + """ + arg = rargs.pop(0) + + # Value explicitly attached to arg? Pretend it's the next + # argument. + if "=" in arg: + (opt, next_arg) = arg.split("=", 1) + rargs.insert(0, next_arg) + had_explicit_value = True + else: + opt = arg + had_explicit_value = False + + try: + opt = self._match_long_opt(opt) + except optparse.BadOptionError: + if self.preserve_unknown_options: + # SCons-specific: if requested, add unknown options to + # the "leftover arguments" list for later processing. + self.largs.append(arg) + if had_explicit_value: + # The unknown option will be re-processed later, + # so undo the insertion of the explicit value. + rargs.pop(0) + return + raise + + option = self._long_opt[opt] + if option.takes_value(): + nargs = option.nargs + if nargs == '?': + if had_explicit_value: + value = rargs.pop(0) + else: + value = option.const + elif len(rargs) < nargs: + if nargs == 1: + self.error(_("%s option requires an argument") % opt) + else: + self.error(_("%s option requires %d arguments") + % (opt, nargs)) + elif nargs == 1: + value = rargs.pop(0) + else: + value = tuple(rargs[0:nargs]) + del rargs[0:nargs] + + elif had_explicit_value: + self.error(_("%s option does not take a value") % opt) + + else: + value = None + + option.process(opt, value, values, self) + + def add_local_option(self, *args, **kw): + """ + Adds a local option to the parser. + + This is initiated by a SetOption() call to add a user-defined + command-line option. We add the option to a separate option + group for the local options, creating the group if necessary. + """ + try: + group = self.local_option_group + except AttributeError: + group = SConsOptionGroup(self, 'Local Options') + group = self.add_option_group(group) + self.local_option_group = group + + result = group.add_option(*args, **kw) + + if result: + # The option was added succesfully. We now have to add the + # default value to our object that holds the default values + # (so that an attempt to fetch the option's attribute will + # yield the default value when not overridden) and then + # we re-parse the leftover command-line options, so that + # any value overridden on the command line is immediately + # available if the user turns around and does a GetOption() + # right away. + setattr(self.values.__defaults__, result.dest, result.default) + self.parse_args(self.largs, self.values) + + return result + +class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter): + def format_usage(self, usage): + return "usage: %s\n" % usage + + def format_heading(self, heading): + """ + This translates any heading of "options" or "Options" into + "SCons Options." Unfortunately, we have to do this here, + because those titles are hard-coded in the optparse calls. + """ + if heading == 'options': + # The versions of optparse.py shipped with Pythons 2.3 and + # 2.4 pass this in uncapitalized; override that so we get + # consistent output on all versions. + heading = "Options" + if heading == 'Options': + heading = "SCons Options" + return optparse.IndentedHelpFormatter.format_heading(self, heading) + + def format_option(self, option): + """ + A copy of the normal optparse.IndentedHelpFormatter.format_option() + method. This has been snarfed so we can modify text wrapping to + out liking: + + -- add our own regular expression that doesn't break on hyphens + (so things like --no-print-directory don't get broken); + + -- wrap the list of options themselves when it's too long + (the wrapper.fill(opts) call below); + + -- set the subsequent_indent when wrapping the help_text. + """ + # The help for each option consists of two parts: + # * the opt strings and metavars + # eg. ("-x", or "-fFILENAME, --file=FILENAME") + # * the user-supplied help string + # eg. ("turn on expert mode", "read data from FILENAME") + # + # If possible, we write both of these on the same line: + # -x turn on expert mode + # + # But if the opt string list is too long, we put the help + # string on a second line, indented to the same column it would + # start in if it fit on the first line. + # -fFILENAME, --file=FILENAME + # read data from FILENAME + result = [] + + try: + opts = self.option_strings[option] + except AttributeError: + # The Python 2.3 version of optparse attaches this to + # to the option argument, not to this object. + opts = option.option_strings + + opt_width = self.help_position - self.current_indent - 2 + if len(opts) > opt_width: + wrapper = textwrap.TextWrapper(width=self.width, + initial_indent = ' ', + subsequent_indent = ' ') + wrapper.wordsep_re = no_hyphen_re + opts = wrapper.fill(opts) + '\n' + indent_first = self.help_position + else: # start help on same line as opts + opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts) + indent_first = 0 + result.append(opts) + if option.help: + + try: + expand_default = self.expand_default + except AttributeError: + # The HelpFormatter base class in the Python 2.3 version + # of optparse has no expand_default() method. + help_text = option.help + else: + help_text = expand_default(option) + + # SCons: indent every line of the help text but the first. + wrapper = textwrap.TextWrapper(width=self.help_width, + subsequent_indent = ' ') + wrapper.wordsep_re = no_hyphen_re + help_lines = wrapper.wrap(help_text) + result.append("%*s%s\n" % (indent_first, "", help_lines[0])) + for line in help_lines[1:]: + result.append("%*s%s\n" % (self.help_position, "", line)) + elif opts[-1] != "\n": + result.append("\n") + return "".join(result) + + # For consistent help output across Python versions, we provide a + # subclass copy of format_option_strings() and these two variables. + # This is necessary (?) for Python2.3, which otherwise concatenates + # a short option with its metavar. + _short_opt_fmt = "%s %s" + _long_opt_fmt = "%s=%s" + + def format_option_strings(self, option): + """Return a comma-separated list of option strings & metavariables.""" + if option.takes_value(): + metavar = option.metavar or option.dest.upper() + short_opts = [] + for sopt in option._short_opts: + short_opts.append(self._short_opt_fmt % (sopt, metavar)) + long_opts = [] + for lopt in option._long_opts: + long_opts.append(self._long_opt_fmt % (lopt, metavar)) + else: + short_opts = option._short_opts + long_opts = option._long_opts + + if self.short_first: + opts = short_opts + long_opts + else: + opts = long_opts + short_opts + + return ", ".join(opts) + +def Parser(version): + """ + Returns an options parser object initialized with the standard + SCons options. + """ + + formatter = SConsIndentedHelpFormatter(max_help_position=30) + + op = SConsOptionParser(option_class=SConsOption, + add_help_option=False, + formatter=formatter, + usage="usage: scons [OPTION] [TARGET] ...",) + + op.preserve_unknown_options = True + op.version = version + + # Add the options to the parser we just created. + # + # These are in the order we want them to show up in the -H help + # text, basically alphabetical. Each op.add_option() call below + # should have a consistent format: + # + # op.add_option("-L", "--long-option-name", + # nargs=1, type="string", + # dest="long_option_name", default='foo', + # action="callback", callback=opt_long_option, + # help="help text goes here", + # metavar="VAR") + # + # Even though the optparse module constructs reasonable default + # destination names from the long option names, we're going to be + # explicit about each one for easier readability and so this code + # will at least show up when grepping the source for option attribute + # names, or otherwise browsing the source code. + + # options ignored for compatibility + def opt_ignore(option, opt, value, parser): + sys.stderr.write("Warning: ignoring %s option\n" % opt) + op.add_option("-b", "-d", "-e", "-m", "-S", "-t", "-w", + "--environment-overrides", + "--no-keep-going", + "--no-print-directory", + "--print-directory", + "--stop", + "--touch", + action="callback", callback=opt_ignore, + help="Ignored for compatibility.") + + op.add_option('-c', '--clean', '--remove', + dest="clean", default=False, + action="store_true", + help="Remove specified targets and dependencies.") + + op.add_option('-C', '--directory', + nargs=1, type="string", + dest="directory", default=[], + action="append", + help="Change to DIR before doing anything.", + metavar="DIR") + + op.add_option('--cache-debug', + nargs=1, + dest="cache_debug", default=None, + action="store", + help="Print CacheDir debug info to FILE.", + metavar="FILE") + + op.add_option('--cache-disable', '--no-cache', + dest='cache_disable', default=False, + action="store_true", + help="Do not retrieve built targets from CacheDir.") + + op.add_option('--cache-force', '--cache-populate', + dest='cache_force', default=False, + action="store_true", + help="Copy already-built targets into the CacheDir.") + + op.add_option('--cache-show', + dest='cache_show', default=False, + action="store_true", + help="Print build actions for files from CacheDir.") + + def opt_invalid(group, value, options): + errmsg = "`%s' is not a valid %s option type, try:\n" % (value, group) + return errmsg + " %s" % ", ".join(options) + + config_options = ["auto", "force" ,"cache"] + + def opt_config(option, opt, value, parser, c_options=config_options): + if not value in c_options: + raise OptionValueError(opt_invalid('config', value, c_options)) + setattr(parser.values, option.dest, value) + opt_config_help = "Controls Configure subsystem: %s." \ + % ", ".join(config_options) + op.add_option('--config', + nargs=1, type="string", + dest="config", default="auto", + action="callback", callback=opt_config, + help = opt_config_help, + metavar="MODE") + + op.add_option('-D', + dest="climb_up", default=None, + action="store_const", const=2, + help="Search up directory tree for SConstruct, " + "build all Default() targets.") + + deprecated_debug_options = { + "dtree" : '; please use --tree=derived instead', + "nomemoizer" : ' and has no effect', + "stree" : '; please use --tree=all,status instead', + "tree" : '; please use --tree=all instead', + } + + debug_options = ["count", "duplicate", "explain", "findlibs", + "includes", "memoizer", "memory", "objects", + "pdb", "prepare", "presub", "stacktrace", + "time"] + + def opt_debug(option, opt, value, parser, + debug_options=debug_options, + deprecated_debug_options=deprecated_debug_options): + if value in debug_options: + parser.values.debug.append(value) + elif value in deprecated_debug_options.keys(): + parser.values.debug.append(value) + try: + parser.values.delayed_warnings + except AttributeError: + parser.values.delayed_warnings = [] + msg = deprecated_debug_options[value] + w = "The --debug=%s option is deprecated%s." % (value, msg) + t = (SCons.Warnings.DeprecatedDebugOptionsWarning, w) + parser.values.delayed_warnings.append(t) + else: + raise OptionValueError(opt_invalid('debug', value, debug_options)) + opt_debug_help = "Print various types of debugging information: %s." \ + % ", ".join(debug_options) + op.add_option('--debug', + nargs=1, type="string", + dest="debug", default=[], + action="callback", callback=opt_debug, + help=opt_debug_help, + metavar="TYPE") + + def opt_diskcheck(option, opt, value, parser): + try: + diskcheck_value = diskcheck_convert(value) + except ValueError, e: + raise OptionValueError("`%s' is not a valid diskcheck type" % e) + setattr(parser.values, option.dest, diskcheck_value) + + op.add_option('--diskcheck', + nargs=1, type="string", + dest='diskcheck', default=None, + action="callback", callback=opt_diskcheck, + help="Enable specific on-disk checks.", + metavar="TYPE") + + def opt_duplicate(option, opt, value, parser): + if not value in SCons.Node.FS.Valid_Duplicates: + raise OptionValueError(opt_invalid('duplication', value, + SCons.Node.FS.Valid_Duplicates)) + setattr(parser.values, option.dest, value) + # Set the duplicate style right away so it can affect linking + # of SConscript files. + SCons.Node.FS.set_duplicate(value) + + opt_duplicate_help = "Set the preferred duplication methods. Must be one of " \ + + ", ".join(SCons.Node.FS.Valid_Duplicates) + + op.add_option('--duplicate', + nargs=1, type="string", + dest="duplicate", default='hard-soft-copy', + action="callback", callback=opt_duplicate, + help=opt_duplicate_help) + + op.add_option('-f', '--file', '--makefile', '--sconstruct', + nargs=1, type="string", + dest="file", default=[], + action="append", + help="Read FILE as the top-level SConstruct file.") + + op.add_option('-h', '--help', + dest="help", default=False, + action="store_true", + help="Print defined help message, or this one.") + + op.add_option("-H", "--help-options", + action="help", + help="Print this message and exit.") + + op.add_option('-i', '--ignore-errors', + dest='ignore_errors', default=False, + action="store_true", + help="Ignore errors from build actions.") + + op.add_option('-I', '--include-dir', + nargs=1, + dest='include_dir', default=[], + action="append", + help="Search DIR for imported Python modules.", + metavar="DIR") + + op.add_option('--implicit-cache', + dest='implicit_cache', default=False, + action="store_true", + help="Cache implicit dependencies") + + def opt_implicit_deps(option, opt, value, parser): + setattr(parser.values, 'implicit_cache', True) + setattr(parser.values, option.dest, True) + + op.add_option('--implicit-deps-changed', + dest="implicit_deps_changed", default=False, + action="callback", callback=opt_implicit_deps, + help="Ignore cached implicit dependencies.") + + op.add_option('--implicit-deps-unchanged', + dest="implicit_deps_unchanged", default=False, + action="callback", callback=opt_implicit_deps, + help="Ignore changes in implicit dependencies.") + + op.add_option('--interact', '--interactive', + dest='interactive', default=False, + action="store_true", + help="Run in interactive mode.") + + op.add_option('-j', '--jobs', + nargs=1, type="int", + dest="num_jobs", default=1, + action="store", + help="Allow N jobs at once.", + metavar="N") + + op.add_option('-k', '--keep-going', + dest='keep_going', default=False, + action="store_true", + help="Keep going when a target can't be made.") + + op.add_option('--max-drift', + nargs=1, type="int", + dest='max_drift', default=SCons.Node.FS.default_max_drift, + action="store", + help="Set maximum system clock drift to N seconds.", + metavar="N") + + op.add_option('--md5-chunksize', + nargs=1, type="int", + dest='md5_chunksize', default=SCons.Node.FS.File.md5_chunksize, + action="store", + help="Set chunk-size for MD5 signature computation to N kilobytes.", + metavar="N") + + op.add_option('-n', '--no-exec', '--just-print', '--dry-run', '--recon', + dest='no_exec', default=False, + action="store_true", + help="Don't build; just print commands.") + + op.add_option('--no-site-dir', + dest='no_site_dir', default=False, + action="store_true", + help="Don't search or use the usual site_scons dir.") + + op.add_option('--profile', + nargs=1, + dest="profile_file", default=None, + action="store", + help="Profile SCons and put results in FILE.", + metavar="FILE") + + op.add_option('-q', '--question', + dest="question", default=False, + action="store_true", + help="Don't build; exit status says if up to date.") + + op.add_option('-Q', + dest='no_progress', default=False, + action="store_true", + help="Suppress \"Reading/Building\" progress messages.") + + op.add_option('--random', + dest="random", default=False, + action="store_true", + help="Build dependencies in random order.") + + op.add_option('-s', '--silent', '--quiet', + dest="silent", default=False, + action="store_true", + help="Don't print commands.") + + op.add_option('--site-dir', + nargs=1, + dest='site_dir', default=None, + action="store", + help="Use DIR instead of the usual site_scons dir.", + metavar="DIR") + + op.add_option('--stack-size', + nargs=1, type="int", + dest='stack_size', + action="store", + help="Set the stack size of the threads used to run jobs to N kilobytes.", + metavar="N") + + op.add_option('--taskmastertrace', + nargs=1, + dest="taskmastertrace_file", default=None, + action="store", + help="Trace Node evaluation to FILE.", + metavar="FILE") + + tree_options = ["all", "derived", "prune", "status"] + + def opt_tree(option, opt, value, parser, tree_options=tree_options): + import Main + tp = Main.TreePrinter() + for o in value.split(','): + if o == 'all': + tp.derived = False + elif o == 'derived': + tp.derived = True + elif o == 'prune': + tp.prune = True + elif o == 'status': + tp.status = True + else: + raise OptionValueError(opt_invalid('--tree', o, tree_options)) + parser.values.tree_printers.append(tp) + + opt_tree_help = "Print a dependency tree in various formats: %s." \ + % ", ".join(tree_options) + + op.add_option('--tree', + nargs=1, type="string", + dest="tree_printers", default=[], + action="callback", callback=opt_tree, + help=opt_tree_help, + metavar="OPTIONS") + + op.add_option('-u', '--up', '--search-up', + dest="climb_up", default=0, + action="store_const", const=1, + help="Search up directory tree for SConstruct, " + "build targets at or below current directory.") + + op.add_option('-U', + dest="climb_up", default=0, + action="store_const", const=3, + help="Search up directory tree for SConstruct, " + "build Default() targets from local SConscript.") + + def opt_version(option, opt, value, parser): + sys.stdout.write(parser.version + '\n') + sys.exit(0) + op.add_option("-v", "--version", + action="callback", callback=opt_version, + help="Print the SCons version number and exit.") + + def opt_warn(option, opt, value, parser, tree_options=tree_options): + if SCons.Util.is_String(value): + value = value.split(',') + parser.values.warn.extend(value) + + op.add_option('--warn', '--warning', + nargs=1, type="string", + dest="warn", default=[], + action="callback", callback=opt_warn, + help="Enable or disable warnings.", + metavar="WARNING-SPEC") + + op.add_option('-Y', '--repository', '--srcdir', + nargs=1, + dest="repository", default=[], + action="append", + help="Search REPOSITORY for source and target files.") + + # Options from Make and Cons classic that we do not yet support, + # but which we may support someday and whose (potential) meanings + # we don't want to change. These all get a "the -X option is not + # yet implemented" message and don't show up in the help output. + + def opt_not_yet(option, opt, value, parser): + msg = "Warning: the %s option is not yet implemented\n" % opt + sys.stderr.write(msg) + + op.add_option('-l', '--load-average', '--max-load', + nargs=1, type="float", + dest="load_average", default=0, + action="callback", callback=opt_not_yet, + # action="store", + # help="Don't start multiple jobs unless load is below " + # "LOAD-AVERAGE." + help=SUPPRESS_HELP) + op.add_option('--list-actions', + dest="list_actions", + action="callback", callback=opt_not_yet, + # help="Don't build; list files and build actions." + help=SUPPRESS_HELP) + op.add_option('--list-derived', + dest="list_derived", + action="callback", callback=opt_not_yet, + # help="Don't build; list files that would be built." + help=SUPPRESS_HELP) + op.add_option('--list-where', + dest="list_where", + action="callback", callback=opt_not_yet, + # help="Don't build; list files and where defined." + help=SUPPRESS_HELP) + op.add_option('-o', '--old-file', '--assume-old', + nargs=1, type="string", + dest="old_file", default=[], + action="callback", callback=opt_not_yet, + # action="append", + # help = "Consider FILE to be old; don't rebuild it." + help=SUPPRESS_HELP) + op.add_option('--override', + nargs=1, type="string", + action="callback", callback=opt_not_yet, + dest="override", + # help="Override variables as specified in FILE." + help=SUPPRESS_HELP) + op.add_option('-p', + action="callback", callback=opt_not_yet, + dest="p", + # help="Print internal environments/objects." + help=SUPPRESS_HELP) + op.add_option('-r', '-R', '--no-builtin-rules', '--no-builtin-variables', + action="callback", callback=opt_not_yet, + dest="no_builtin_rules", + # help="Clear default environments and variables." + help=SUPPRESS_HELP) + op.add_option('--write-filenames', + nargs=1, type="string", + dest="write_filenames", + action="callback", callback=opt_not_yet, + # help="Write all filenames examined into FILE." + help=SUPPRESS_HELP) + op.add_option('-W', '--new-file', '--assume-new', '--what-if', + nargs=1, type="string", + dest="new_file", + action="callback", callback=opt_not_yet, + # help="Consider FILE to be changed." + help=SUPPRESS_HELP) + op.add_option('--warn-undefined-variables', + dest="warn_undefined_variables", + action="callback", callback=opt_not_yet, + # help="Warn when an undefined variable is referenced." + help=SUPPRESS_HELP) + + return op + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Script/SConscript.py b/scons/scons-local-2.3.0/SCons/Script/SConscript.py new file mode 100644 index 000000000..59039eaf9 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Script/SConscript.py @@ -0,0 +1,635 @@ +"""SCons.Script.SConscript + +This module defines the Python API provided to SConscript and SConstruct +files. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +from __future__ import division + +__revision__ = "src/engine/SCons/Script/SConscript.py 2013/03/03 09:48:35 garyo" + +import SCons +import SCons.Action +import SCons.Builder +import SCons.Defaults +import SCons.Environment +import SCons.Errors +import SCons.Node +import SCons.Node.Alias +import SCons.Node.FS +import SCons.Platform +import SCons.SConf +import SCons.Script.Main +import SCons.Tool +import SCons.Util + +import collections +import os +import os.path +import re +import sys +import traceback + +# The following variables used to live in this module. Some +# SConscript files out there may have referred to them directly as +# SCons.Script.SConscript.*. This is now supported by some special +# handling towards the bottom of the SConscript.__init__.py module. +#Arguments = {} +#ArgList = [] +#BuildTargets = TargetList() +#CommandLineTargets = [] +#DefaultTargets = [] + +class SConscriptReturn(Exception): + pass + +launch_dir = os.path.abspath(os.curdir) + +GlobalDict = None + +# global exports set by Export(): +global_exports = {} + +# chdir flag +sconscript_chdir = 1 + +def get_calling_namespaces(): + """Return the locals and globals for the function that called + into this module in the current call stack.""" + try: 1//0 + except ZeroDivisionError: + # Don't start iterating with the current stack-frame to + # prevent creating reference cycles (f_back is safe). + frame = sys.exc_info()[2].tb_frame.f_back + + # Find the first frame that *isn't* from this file. This means + # that we expect all of the SCons frames that implement an Export() + # or SConscript() call to be in this file, so that we can identify + # the first non-Script.SConscript frame as the user's local calling + # environment, and the locals and globals dictionaries from that + # frame as the calling namespaces. See the comment below preceding + # the DefaultEnvironmentCall block for even more explanation. + while frame.f_globals.get("__name__") == __name__: + frame = frame.f_back + + return frame.f_locals, frame.f_globals + + +def compute_exports(exports): + """Compute a dictionary of exports given one of the parameters + to the Export() function or the exports argument to SConscript().""" + + loc, glob = get_calling_namespaces() + + retval = {} + try: + for export in exports: + if SCons.Util.is_Dict(export): + retval.update(export) + else: + try: + retval[export] = loc[export] + except KeyError: + retval[export] = glob[export] + except KeyError, x: + raise SCons.Errors.UserError("Export of non-existent variable '%s'"%x) + + return retval + +class Frame(object): + """A frame on the SConstruct/SConscript call stack""" + def __init__(self, fs, exports, sconscript): + self.globals = BuildDefaultGlobals() + self.retval = None + self.prev_dir = fs.getcwd() + self.exports = compute_exports(exports) # exports from the calling SConscript + # make sure the sconscript attr is a Node. + if isinstance(sconscript, SCons.Node.Node): + self.sconscript = sconscript + elif sconscript == '-': + self.sconscript = None + else: + self.sconscript = fs.File(str(sconscript)) + +# the SConstruct/SConscript call stack: +call_stack = [] + +# For documentation on the methods in this file, see the scons man-page + +def Return(*vars, **kw): + retval = [] + try: + fvars = SCons.Util.flatten(vars) + for var in fvars: + for v in var.split(): + retval.append(call_stack[-1].globals[v]) + except KeyError, x: + raise SCons.Errors.UserError("Return of non-existent variable '%s'"%x) + + if len(retval) == 1: + call_stack[-1].retval = retval[0] + else: + call_stack[-1].retval = tuple(retval) + + stop = kw.get('stop', True) + + if stop: + raise SConscriptReturn + + +stack_bottom = '% Stack boTTom %' # hard to define a variable w/this name :) + +def _SConscript(fs, *files, **kw): + top = fs.Top + sd = fs.SConstruct_dir.rdir() + exports = kw.get('exports', []) + + # evaluate each SConscript file + results = [] + for fn in files: + call_stack.append(Frame(fs, exports, fn)) + old_sys_path = sys.path + try: + SCons.Script.sconscript_reading = SCons.Script.sconscript_reading + 1 + if fn == "-": + exec sys.stdin in call_stack[-1].globals + else: + if isinstance(fn, SCons.Node.Node): + f = fn + else: + f = fs.File(str(fn)) + _file_ = None + + # Change directory to the top of the source + # tree to make sure the os's cwd and the cwd of + # fs match so we can open the SConscript. + fs.chdir(top, change_os_dir=1) + if f.rexists(): + actual = f.rfile() + _file_ = open(actual.get_abspath(), "r") + elif f.srcnode().rexists(): + actual = f.srcnode().rfile() + _file_ = open(actual.get_abspath(), "r") + elif f.has_src_builder(): + # The SConscript file apparently exists in a source + # code management system. Build it, but then clear + # the builder so that it doesn't get built *again* + # during the actual build phase. + f.build() + f.built() + f.builder_set(None) + if f.exists(): + _file_ = open(f.get_abspath(), "r") + if _file_: + # Chdir to the SConscript directory. Use a path + # name relative to the SConstruct file so that if + # we're using the -f option, we're essentially + # creating a parallel SConscript directory structure + # in our local directory tree. + # + # XXX This is broken for multiple-repository cases + # where the SConstruct and SConscript files might be + # in different Repositories. For now, cross that + # bridge when someone comes to it. + try: + src_dir = kw['src_dir'] + except KeyError: + ldir = fs.Dir(f.dir.get_path(sd)) + else: + ldir = fs.Dir(src_dir) + if not ldir.is_under(f.dir): + # They specified a source directory, but + # it's above the SConscript directory. + # Do the sensible thing and just use the + # SConcript directory. + ldir = fs.Dir(f.dir.get_path(sd)) + try: + fs.chdir(ldir, change_os_dir=sconscript_chdir) + except OSError: + # There was no local directory, so we should be + # able to chdir to the Repository directory. + # Note that we do this directly, not through + # fs.chdir(), because we still need to + # interpret the stuff within the SConscript file + # relative to where we are logically. + fs.chdir(ldir, change_os_dir=0) + os.chdir(actual.dir.get_abspath()) + + # Append the SConscript directory to the beginning + # of sys.path so Python modules in the SConscript + # directory can be easily imported. + sys.path = [ f.dir.get_abspath() ] + sys.path + + # This is the magic line that actually reads up + # and executes the stuff in the SConscript file. + # The locals for this frame contain the special + # bottom-of-the-stack marker so that any + # exceptions that occur when processing this + # SConscript can base the printed frames at this + # level and not show SCons internals as well. + call_stack[-1].globals.update({stack_bottom:1}) + old_file = call_stack[-1].globals.get('__file__') + try: + del call_stack[-1].globals['__file__'] + except KeyError: + pass + try: + try: + exec _file_ in call_stack[-1].globals + except SConscriptReturn: + pass + finally: + if old_file is not None: + call_stack[-1].globals.update({__file__:old_file}) + else: + SCons.Warnings.warn(SCons.Warnings.MissingSConscriptWarning, + "Ignoring missing SConscript '%s'" % f.path) + + finally: + SCons.Script.sconscript_reading = SCons.Script.sconscript_reading - 1 + sys.path = old_sys_path + frame = call_stack.pop() + try: + fs.chdir(frame.prev_dir, change_os_dir=sconscript_chdir) + except OSError: + # There was no local directory, so chdir to the + # Repository directory. Like above, we do this + # directly. + fs.chdir(frame.prev_dir, change_os_dir=0) + rdir = frame.prev_dir.rdir() + rdir._create() # Make sure there's a directory there. + try: + os.chdir(rdir.get_abspath()) + except OSError, e: + # We still couldn't chdir there, so raise the error, + # but only if actions are being executed. + # + # If the -n option was used, the directory would *not* + # have been created and we should just carry on and + # let things muddle through. This isn't guaranteed + # to work if the SConscript files are reading things + # from disk (for example), but it should work well + # enough for most configurations. + if SCons.Action.execute_actions: + raise e + + results.append(frame.retval) + + # if we only have one script, don't return a tuple + if len(results) == 1: + return results[0] + else: + return tuple(results) + +def SConscript_exception(file=sys.stderr): + """Print an exception stack trace just for the SConscript file(s). + This will show users who have Python errors where the problem is, + without cluttering the output with all of the internal calls leading + up to where we exec the SConscript.""" + exc_type, exc_value, exc_tb = sys.exc_info() + tb = exc_tb + while tb and stack_bottom not in tb.tb_frame.f_locals: + tb = tb.tb_next + if not tb: + # We did not find our exec statement, so this was actually a bug + # in SCons itself. Show the whole stack. + tb = exc_tb + stack = traceback.extract_tb(tb) + try: + type = exc_type.__name__ + except AttributeError: + type = str(exc_type) + if type[:11] == "exceptions.": + type = type[11:] + file.write('%s: %s:\n' % (type, exc_value)) + for fname, line, func, text in stack: + file.write(' File "%s", line %d:\n' % (fname, line)) + file.write(' %s\n' % text) + +def annotate(node): + """Annotate a node with the stack frame describing the + SConscript file and line number that created it.""" + tb = sys.exc_info()[2] + while tb and stack_bottom not in tb.tb_frame.f_locals: + tb = tb.tb_next + if not tb: + # We did not find any exec of an SConscript file: what?! + raise SCons.Errors.InternalError("could not find SConscript stack frame") + node.creator = traceback.extract_stack(tb)[0] + +# The following line would cause each Node to be annotated using the +# above function. Unfortunately, this is a *huge* performance hit, so +# leave this disabled until we find a more efficient mechanism. +#SCons.Node.Annotate = annotate + +class SConsEnvironment(SCons.Environment.Base): + """An Environment subclass that contains all of the methods that + are particular to the wrapper SCons interface and which aren't + (or shouldn't be) part of the build engine itself. + + Note that not all of the methods of this class have corresponding + global functions, there are some private methods. + """ + + # + # Private methods of an SConsEnvironment. + # + def _exceeds_version(self, major, minor, v_major, v_minor): + """Return 1 if 'major' and 'minor' are greater than the version + in 'v_major' and 'v_minor', and 0 otherwise.""" + return (major > v_major or (major == v_major and minor > v_minor)) + + def _get_major_minor_revision(self, version_string): + """Split a version string into major, minor and (optionally) + revision parts. + + This is complicated by the fact that a version string can be + something like 3.2b1.""" + version = version_string.split(' ')[0].split('.') + v_major = int(version[0]) + v_minor = int(re.match('\d+', version[1]).group()) + if len(version) >= 3: + v_revision = int(re.match('\d+', version[2]).group()) + else: + v_revision = 0 + return v_major, v_minor, v_revision + + def _get_SConscript_filenames(self, ls, kw): + """ + Convert the parameters passed to SConscript() calls into a list + of files and export variables. If the parameters are invalid, + throws SCons.Errors.UserError. Returns a tuple (l, e) where l + is a list of SConscript filenames and e is a list of exports. + """ + exports = [] + + if len(ls) == 0: + try: + dirs = kw["dirs"] + except KeyError: + raise SCons.Errors.UserError("Invalid SConscript usage - no parameters") + + if not SCons.Util.is_List(dirs): + dirs = [ dirs ] + dirs = list(map(str, dirs)) + + name = kw.get('name', 'SConscript') + + files = [os.path.join(n, name) for n in dirs] + + elif len(ls) == 1: + + files = ls[0] + + elif len(ls) == 2: + + files = ls[0] + exports = self.Split(ls[1]) + + else: + + raise SCons.Errors.UserError("Invalid SConscript() usage - too many arguments") + + if not SCons.Util.is_List(files): + files = [ files ] + + if kw.get('exports'): + exports.extend(self.Split(kw['exports'])) + + variant_dir = kw.get('variant_dir') or kw.get('build_dir') + if variant_dir: + if len(files) != 1: + raise SCons.Errors.UserError("Invalid SConscript() usage - can only specify one SConscript with a variant_dir") + duplicate = kw.get('duplicate', 1) + src_dir = kw.get('src_dir') + if not src_dir: + src_dir, fname = os.path.split(str(files[0])) + files = [os.path.join(str(variant_dir), fname)] + else: + if not isinstance(src_dir, SCons.Node.Node): + src_dir = self.fs.Dir(src_dir) + fn = files[0] + if not isinstance(fn, SCons.Node.Node): + fn = self.fs.File(fn) + if fn.is_under(src_dir): + # Get path relative to the source directory. + fname = fn.get_path(src_dir) + files = [os.path.join(str(variant_dir), fname)] + else: + files = [fn.abspath] + kw['src_dir'] = variant_dir + self.fs.VariantDir(variant_dir, src_dir, duplicate) + + return (files, exports) + + # + # Public methods of an SConsEnvironment. These get + # entry points in the global name space so they can be called + # as global functions. + # + + def Configure(self, *args, **kw): + if not SCons.Script.sconscript_reading: + raise SCons.Errors.UserError("Calling Configure from Builders is not supported.") + kw['_depth'] = kw.get('_depth', 0) + 1 + return SCons.Environment.Base.Configure(self, *args, **kw) + + def Default(self, *targets): + SCons.Script._Set_Default_Targets(self, targets) + + def EnsureSConsVersion(self, major, minor, revision=0): + """Exit abnormally if the SCons version is not late enough.""" + scons_ver = self._get_major_minor_revision(SCons.__version__) + if scons_ver < (major, minor, revision): + if revision: + scons_ver_string = '%d.%d.%d' % (major, minor, revision) + else: + scons_ver_string = '%d.%d' % (major, minor) + print "SCons %s or greater required, but you have SCons %s" % \ + (scons_ver_string, SCons.__version__) + sys.exit(2) + + def EnsurePythonVersion(self, major, minor): + """Exit abnormally if the Python version is not late enough.""" + if sys.version_info < (major, minor): + v = sys.version.split()[0] + print "Python %d.%d or greater required, but you have Python %s" %(major,minor,v) + sys.exit(2) + + def Exit(self, value=0): + sys.exit(value) + + def Export(self, *vars, **kw): + for var in vars: + global_exports.update(compute_exports(self.Split(var))) + global_exports.update(kw) + + def GetLaunchDir(self): + global launch_dir + return launch_dir + + def GetOption(self, name): + name = self.subst(name) + return SCons.Script.Main.GetOption(name) + + def Help(self, text): + text = self.subst(text, raw=1) + SCons.Script.HelpFunction(text) + + def Import(self, *vars): + try: + frame = call_stack[-1] + globals = frame.globals + exports = frame.exports + for var in vars: + var = self.Split(var) + for v in var: + if v == '*': + globals.update(global_exports) + globals.update(exports) + else: + if v in exports: + globals[v] = exports[v] + else: + globals[v] = global_exports[v] + except KeyError,x: + raise SCons.Errors.UserError("Import of non-existent variable '%s'"%x) + + def SConscript(self, *ls, **kw): + if 'build_dir' in kw: + msg = """The build_dir keyword has been deprecated; use the variant_dir keyword instead.""" + SCons.Warnings.warn(SCons.Warnings.DeprecatedBuildDirWarning, msg) + def subst_element(x, subst=self.subst): + if SCons.Util.is_List(x): + x = list(map(subst, x)) + else: + x = subst(x) + return x + ls = list(map(subst_element, ls)) + subst_kw = {} + for key, val in kw.items(): + if SCons.Util.is_String(val): + val = self.subst(val) + elif SCons.Util.is_List(val): + result = [] + for v in val: + if SCons.Util.is_String(v): + v = self.subst(v) + result.append(v) + val = result + subst_kw[key] = val + + files, exports = self._get_SConscript_filenames(ls, subst_kw) + subst_kw['exports'] = exports + return _SConscript(self.fs, *files, **subst_kw) + + def SConscriptChdir(self, flag): + global sconscript_chdir + sconscript_chdir = flag + + def SetOption(self, name, value): + name = self.subst(name) + SCons.Script.Main.SetOption(name, value) + +# +# +# +SCons.Environment.Environment = SConsEnvironment + +def Configure(*args, **kw): + if not SCons.Script.sconscript_reading: + raise SCons.Errors.UserError("Calling Configure from Builders is not supported.") + kw['_depth'] = 1 + return SCons.SConf.SConf(*args, **kw) + +# It's very important that the DefaultEnvironmentCall() class stay in this +# file, with the get_calling_namespaces() function, the compute_exports() +# function, the Frame class and the SConsEnvironment.Export() method. +# These things make up the calling stack leading up to the actual global +# Export() or SConscript() call that the user issued. We want to allow +# users to export local variables that they define, like so: +# +# def func(): +# x = 1 +# Export('x') +# +# To support this, the get_calling_namespaces() function assumes that +# the *first* stack frame that's not from this file is the local frame +# for the Export() or SConscript() call. + +_DefaultEnvironmentProxy = None + +def get_DefaultEnvironmentProxy(): + global _DefaultEnvironmentProxy + if not _DefaultEnvironmentProxy: + default_env = SCons.Defaults.DefaultEnvironment() + _DefaultEnvironmentProxy = SCons.Environment.NoSubstitutionProxy(default_env) + return _DefaultEnvironmentProxy + +class DefaultEnvironmentCall(object): + """A class that implements "global function" calls of + Environment methods by fetching the specified method from the + DefaultEnvironment's class. Note that this uses an intermediate + proxy class instead of calling the DefaultEnvironment method + directly so that the proxy can override the subst() method and + thereby prevent expansion of construction variables (since from + the user's point of view this was called as a global function, + with no associated construction environment).""" + def __init__(self, method_name, subst=0): + self.method_name = method_name + if subst: + self.factory = SCons.Defaults.DefaultEnvironment + else: + self.factory = get_DefaultEnvironmentProxy + def __call__(self, *args, **kw): + env = self.factory() + method = getattr(env, self.method_name) + return method(*args, **kw) + + +def BuildDefaultGlobals(): + """ + Create a dictionary containing all the default globals for + SConstruct and SConscript files. + """ + + global GlobalDict + if GlobalDict is None: + GlobalDict = {} + + import SCons.Script + d = SCons.Script.__dict__ + def not_a_module(m, d=d, mtype=type(SCons.Script)): + return not isinstance(d[m], mtype) + for m in filter(not_a_module, dir(SCons.Script)): + GlobalDict[m] = d[m] + + return GlobalDict.copy() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Script/__init__.py b/scons/scons-local-2.3.0/SCons/Script/__init__.py new file mode 100644 index 000000000..5b3eac812 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Script/__init__.py @@ -0,0 +1,412 @@ +"""SCons.Script + +This file implements the main() function used by the scons script. + +Architecturally, this *is* the scons script, and will likely only be +called from the external "scons" wrapper. Consequently, anything here +should not be, or be considered, part of the build engine. If it's +something that we expect other software to want to use, it should go in +some other module. If it's specific to the "scons" script invocation, +it goes here. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Script/__init__.py 2013/03/03 09:48:35 garyo" + +import time +start_time = time.time() + +import collections +import os +import sys + +# Special chicken-and-egg handling of the "--debug=memoizer" flag: +# +# SCons.Memoize contains a metaclass implementation that affects how +# the other classes are instantiated. The Memoizer may add shim methods +# to classes that have methods that cache computed values in order to +# count and report the hits and misses. +# +# If we wait to enable the Memoization until after we've parsed the +# command line options normally, it will be too late, because the Memoizer +# will have already analyzed the classes that it's Memoizing and decided +# to not add the shims. So we use a special-case, up-front check for +# the "--debug=memoizer" flag and enable Memoizer before we import any +# of the other modules that use it. + +_args = sys.argv + os.environ.get('SCONSFLAGS', '').split() +if "--debug=memoizer" in _args: + import SCons.Memoize + import SCons.Warnings + try: + SCons.Memoize.EnableMemoization() + except SCons.Warnings.Warning: + # Some warning was thrown. Arrange for it to be displayed + # or not after warnings are configured. + import Main + exc_type, exc_value, tb = sys.exc_info() + Main.delayed_warnings.append((exc_type, exc_value)) +del _args + +import SCons.Action +import SCons.Builder +import SCons.Environment +import SCons.Node.FS +import SCons.Options +import SCons.Platform +import SCons.Scanner +import SCons.SConf +import SCons.Subst +import SCons.Tool +import SCons.Util +import SCons.Variables +import SCons.Defaults + +import Main + +main = Main.main + +# The following are global class definitions and variables that used to +# live directly in this module back before 0.96.90, when it contained +# a lot of code. Some SConscript files in widely-distributed packages +# (Blender is the specific example) actually reached into SCons.Script +# directly to use some of these. Rather than break those SConscript +# files, we're going to propagate these names into the SCons.Script +# namespace here. +# +# Some of these are commented out because it's *really* unlikely anyone +# used them, but we're going to leave the comment here to try to make +# it obvious what to do if the situation arises. +BuildTask = Main.BuildTask +CleanTask = Main.CleanTask +QuestionTask = Main.QuestionTask +#PrintHelp = Main.PrintHelp +#SConscriptSettableOptions = Main.SConscriptSettableOptions + +AddOption = Main.AddOption +GetOption = Main.GetOption +SetOption = Main.SetOption +Progress = Main.Progress +GetBuildFailures = Main.GetBuildFailures + +#keep_going_on_error = Main.keep_going_on_error +#print_dtree = Main.print_dtree +#print_explanations = Main.print_explanations +#print_includes = Main.print_includes +#print_objects = Main.print_objects +#print_time = Main.print_time +#print_tree = Main.print_tree +#memory_stats = Main.memory_stats +#ignore_errors = Main.ignore_errors +#sconscript_time = Main.sconscript_time +#command_time = Main.command_time +#exit_status = Main.exit_status +#profiling = Main.profiling +#repositories = Main.repositories + +# +import SConscript +_SConscript = SConscript + +call_stack = _SConscript.call_stack + +# +Action = SCons.Action.Action +AddMethod = SCons.Util.AddMethod +AllowSubstExceptions = SCons.Subst.SetAllowableExceptions +Builder = SCons.Builder.Builder +Configure = _SConscript.Configure +Environment = SCons.Environment.Environment +#OptParser = SCons.SConsOptions.OptParser +FindPathDirs = SCons.Scanner.FindPathDirs +Platform = SCons.Platform.Platform +Return = _SConscript.Return +Scanner = SCons.Scanner.Base +Tool = SCons.Tool.Tool +WhereIs = SCons.Util.WhereIs + +# +BoolVariable = SCons.Variables.BoolVariable +EnumVariable = SCons.Variables.EnumVariable +ListVariable = SCons.Variables.ListVariable +PackageVariable = SCons.Variables.PackageVariable +PathVariable = SCons.Variables.PathVariable + +# Deprecated names that will go away some day. +BoolOption = SCons.Options.BoolOption +EnumOption = SCons.Options.EnumOption +ListOption = SCons.Options.ListOption +PackageOption = SCons.Options.PackageOption +PathOption = SCons.Options.PathOption + +# Action factories. +Chmod = SCons.Defaults.Chmod +Copy = SCons.Defaults.Copy +Delete = SCons.Defaults.Delete +Mkdir = SCons.Defaults.Mkdir +Move = SCons.Defaults.Move +Touch = SCons.Defaults.Touch + +# Pre-made, public scanners. +CScanner = SCons.Tool.CScanner +DScanner = SCons.Tool.DScanner +DirScanner = SCons.Defaults.DirScanner +ProgramScanner = SCons.Tool.ProgramScanner +SourceFileScanner = SCons.Tool.SourceFileScanner + +# Functions we might still convert to Environment methods. +CScan = SCons.Defaults.CScan +DefaultEnvironment = SCons.Defaults.DefaultEnvironment + +# Other variables we provide. +class TargetList(collections.UserList): + def _do_nothing(self, *args, **kw): + pass + def _add_Default(self, list): + self.extend(list) + def _clear(self): + del self[:] + +ARGUMENTS = {} +ARGLIST = [] +BUILD_TARGETS = TargetList() +COMMAND_LINE_TARGETS = [] +DEFAULT_TARGETS = [] + +# BUILD_TARGETS can be modified in the SConscript files. If so, we +# want to treat the modified BUILD_TARGETS list as if they specified +# targets on the command line. To do that, though, we need to know if +# BUILD_TARGETS was modified through "official" APIs or by hand. We do +# this by updating two lists in parallel, the documented BUILD_TARGETS +# list, above, and this internal _build_plus_default targets list which +# should only have "official" API changes. Then Script/Main.py can +# compare these two afterwards to figure out if the user added their +# own targets to BUILD_TARGETS. +_build_plus_default = TargetList() + +def _Add_Arguments(alist): + for arg in alist: + a, b = arg.split('=', 1) + ARGUMENTS[a] = b + ARGLIST.append((a, b)) + +def _Add_Targets(tlist): + if tlist: + COMMAND_LINE_TARGETS.extend(tlist) + BUILD_TARGETS.extend(tlist) + BUILD_TARGETS._add_Default = BUILD_TARGETS._do_nothing + BUILD_TARGETS._clear = BUILD_TARGETS._do_nothing + _build_plus_default.extend(tlist) + _build_plus_default._add_Default = _build_plus_default._do_nothing + _build_plus_default._clear = _build_plus_default._do_nothing + +def _Set_Default_Targets_Has_Been_Called(d, fs): + return DEFAULT_TARGETS + +def _Set_Default_Targets_Has_Not_Been_Called(d, fs): + if d is None: + d = [fs.Dir('.')] + return d + +_Get_Default_Targets = _Set_Default_Targets_Has_Not_Been_Called + +def _Set_Default_Targets(env, tlist): + global DEFAULT_TARGETS + global _Get_Default_Targets + _Get_Default_Targets = _Set_Default_Targets_Has_Been_Called + for t in tlist: + if t is None: + # Delete the elements from the list in-place, don't + # reassign an empty list to DEFAULT_TARGETS, so that the + # variables will still point to the same object we point to. + del DEFAULT_TARGETS[:] + BUILD_TARGETS._clear() + _build_plus_default._clear() + elif isinstance(t, SCons.Node.Node): + DEFAULT_TARGETS.append(t) + BUILD_TARGETS._add_Default([t]) + _build_plus_default._add_Default([t]) + else: + nodes = env.arg2nodes(t, env.fs.Entry) + DEFAULT_TARGETS.extend(nodes) + BUILD_TARGETS._add_Default(nodes) + _build_plus_default._add_Default(nodes) + +# +help_text = None + +def HelpFunction(text): + global help_text + if SCons.Script.help_text is None: + SCons.Script.help_text = text + else: + help_text = help_text + text + +# +# Will be non-zero if we are reading an SConscript file. +sconscript_reading = 0 + +# +def Variables(files=[], args=ARGUMENTS): + return SCons.Variables.Variables(files, args) + +def Options(files=[], args=ARGUMENTS): + return SCons.Options.Options(files, args) + +# The list of global functions to add to the SConscript name space +# that end up calling corresponding methods or Builders in the +# DefaultEnvironment(). +GlobalDefaultEnvironmentFunctions = [ + # Methods from the SConsEnvironment class, above. + 'Default', + 'EnsurePythonVersion', + 'EnsureSConsVersion', + 'Exit', + 'Export', + 'GetLaunchDir', + 'Help', + 'Import', + #'SConscript', is handled separately, below. + 'SConscriptChdir', + + # Methods from the Environment.Base class. + 'AddPostAction', + 'AddPreAction', + 'Alias', + 'AlwaysBuild', + 'BuildDir', + 'CacheDir', + 'Clean', + #The Command() method is handled separately, below. + 'Decider', + 'Depends', + 'Dir', + 'NoClean', + 'NoCache', + 'Entry', + 'Execute', + 'File', + 'FindFile', + 'FindInstalledFiles', + 'FindSourceFiles', + 'Flatten', + 'GetBuildPath', + 'Glob', + 'Ignore', + 'Install', + 'InstallAs', + 'Literal', + 'Local', + 'ParseDepends', + 'Precious', + 'Repository', + 'Requires', + 'SConsignFile', + 'SideEffect', + 'SourceCode', + 'SourceSignatures', + 'Split', + 'Tag', + 'TargetSignatures', + 'Value', + 'VariantDir', +] + +GlobalDefaultBuilders = [ + # Supported builders. + 'CFile', + 'CXXFile', + 'DVI', + 'Jar', + 'Java', + 'JavaH', + 'Library', + 'M4', + 'MSVSProject', + 'Object', + 'PCH', + 'PDF', + 'PostScript', + 'Program', + 'RES', + 'RMIC', + 'SharedLibrary', + 'SharedObject', + 'StaticLibrary', + 'StaticObject', + 'Tar', + 'TypeLibrary', + 'Zip', + 'Package', +] + +for name in GlobalDefaultEnvironmentFunctions + GlobalDefaultBuilders: + exec "%s = _SConscript.DefaultEnvironmentCall(%s)" % (name, repr(name)) +del name + +# There are a handful of variables that used to live in the +# Script/SConscript.py module that some SConscript files out there were +# accessing directly as SCons.Script.SConscript.*. The problem is that +# "SConscript" in this namespace is no longer a module, it's a global +# function call--or more precisely, an object that implements a global +# function call through the default Environment. Nevertheless, we can +# maintain backwards compatibility for SConscripts that were reaching in +# this way by hanging some attributes off the "SConscript" object here. +SConscript = _SConscript.DefaultEnvironmentCall('SConscript') + +# Make SConscript look enough like the module it used to be so +# that pychecker doesn't barf. +SConscript.__name__ = 'SConscript' + +SConscript.Arguments = ARGUMENTS +SConscript.ArgList = ARGLIST +SConscript.BuildTargets = BUILD_TARGETS +SConscript.CommandLineTargets = COMMAND_LINE_TARGETS +SConscript.DefaultTargets = DEFAULT_TARGETS + +# The global Command() function must be handled differently than the +# global functions for other construction environment methods because +# we want people to be able to use Actions that must expand $TARGET +# and $SOURCE later, when (and if) the Action is invoked to build +# the target(s). We do this with the subst=1 argument, which creates +# a DefaultEnvironmentCall instance that wraps up a normal default +# construction environment that performs variable substitution, not a +# proxy that doesn't. +# +# There's a flaw here, though, because any other $-variables on a command +# line will *also* be expanded, each to a null string, but that should +# only be a problem in the unusual case where someone was passing a '$' +# on a command line and *expected* the $ to get through to the shell +# because they were calling Command() and not env.Command()... This is +# unlikely enough that we're going to leave this as is and cross that +# bridge if someone actually comes to it. +Command = _SConscript.DefaultEnvironmentCall('Command', subst=1) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Sig.py b/scons/scons-local-2.3.0/SCons/Sig.py new file mode 100644 index 000000000..a14a99f42 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Sig.py @@ -0,0 +1,63 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Sig.py 2013/03/03 09:48:35 garyo" + +__doc__ = """Place-holder for the old SCons.Sig module hierarchy + +This is no longer used, but code out there (such as the NSIS module on +the SCons wiki) may try to import SCons.Sig. If so, we generate a warning +that points them to the line that caused the import, and don't die. + +If someone actually tried to use the sub-modules or functions within +the package (for example, SCons.Sig.MD5.signature()), then they'll still +get an AttributeError, but at least they'll know where to start looking. +""" + +import SCons.Util +import SCons.Warnings + +msg = 'The SCons.Sig module no longer exists.\n' \ + ' Remove the following "import SCons.Sig" line to eliminate this warning:' + +SCons.Warnings.warn(SCons.Warnings.DeprecatedSigModuleWarning, msg) + +default_calc = None +default_module = None + +class MD5Null(SCons.Util.Null): + def __repr__(self): + return "MD5Null()" + +class TimeStampNull(SCons.Util.Null): + def __repr__(self): + return "TimeStampNull()" + +MD5 = MD5Null() +TimeStamp = TimeStampNull() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Subst.py b/scons/scons-local-2.3.0/SCons/Subst.py new file mode 100644 index 000000000..94a0df800 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Subst.py @@ -0,0 +1,904 @@ +"""SCons.Subst + +SCons string substitution. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Subst.py 2013/03/03 09:48:35 garyo" + +import collections +import re + +import SCons.Errors + +from SCons.Util import is_String, is_Sequence + +# Indexed by the SUBST_* constants below. +_strconv = [SCons.Util.to_String_for_subst, + SCons.Util.to_String_for_subst, + SCons.Util.to_String_for_signature] + + + +AllowableExceptions = (IndexError, NameError) + +def SetAllowableExceptions(*excepts): + global AllowableExceptions + AllowableExceptions = [_f for _f in excepts if _f] + +def raise_exception(exception, target, s): + name = exception.__class__.__name__ + msg = "%s `%s' trying to evaluate `%s'" % (name, exception, s) + if target: + raise SCons.Errors.BuildError(target[0], msg) + else: + raise SCons.Errors.UserError(msg) + + + +class Literal(object): + """A wrapper for a string. If you use this object wrapped + around a string, then it will be interpreted as literal. + When passed to the command interpreter, all special + characters will be escaped.""" + def __init__(self, lstr): + self.lstr = lstr + + def __str__(self): + return self.lstr + + def escape(self, escape_func): + return escape_func(self.lstr) + + def for_signature(self): + return self.lstr + + def is_literal(self): + return 1 + +class SpecialAttrWrapper(object): + """This is a wrapper for what we call a 'Node special attribute.' + This is any of the attributes of a Node that we can reference from + Environment variable substitution, such as $TARGET.abspath or + $SOURCES[1].filebase. We implement the same methods as Literal + so we can handle special characters, plus a for_signature method, + such that we can return some canonical string during signature + calculation to avoid unnecessary rebuilds.""" + + def __init__(self, lstr, for_signature=None): + """The for_signature parameter, if supplied, will be the + canonical string we return from for_signature(). Else + we will simply return lstr.""" + self.lstr = lstr + if for_signature: + self.forsig = for_signature + else: + self.forsig = lstr + + def __str__(self): + return self.lstr + + def escape(self, escape_func): + return escape_func(self.lstr) + + def for_signature(self): + return self.forsig + + def is_literal(self): + return 1 + +def quote_spaces(arg): + """Generic function for putting double quotes around any string that + has white space in it.""" + if ' ' in arg or '\t' in arg: + return '"%s"' % arg + else: + return str(arg) + +class CmdStringHolder(collections.UserString): + """This is a special class used to hold strings generated by + scons_subst() and scons_subst_list(). It defines a special method + escape(). When passed a function with an escape algorithm for a + particular platform, it will return the contained string with the + proper escape sequences inserted. + """ + def __init__(self, cmd, literal=None): + collections.UserString.__init__(self, cmd) + self.literal = literal + + def is_literal(self): + return self.literal + + def escape(self, escape_func, quote_func=quote_spaces): + """Escape the string with the supplied function. The + function is expected to take an arbitrary string, then + return it with all special characters escaped and ready + for passing to the command interpreter. + + After calling this function, the next call to str() will + return the escaped string. + """ + + if self.is_literal(): + return escape_func(self.data) + elif ' ' in self.data or '\t' in self.data: + return quote_func(self.data) + else: + return self.data + +def escape_list(mylist, escape_func): + """Escape a list of arguments by running the specified escape_func + on every object in the list that has an escape() method.""" + def escape(obj, escape_func=escape_func): + try: + e = obj.escape + except AttributeError: + return obj + else: + return e(escape_func) + return list(map(escape, mylist)) + +class NLWrapper(object): + """A wrapper class that delays turning a list of sources or targets + into a NodeList until it's needed. The specified function supplied + when the object is initialized is responsible for turning raw nodes + into proxies that implement the special attributes like .abspath, + .source, etc. This way, we avoid creating those proxies just + "in case" someone is going to use $TARGET or the like, and only + go through the trouble if we really have to. + + In practice, this might be a wash performance-wise, but it's a little + cleaner conceptually... + """ + + def __init__(self, list, func): + self.list = list + self.func = func + def _return_nodelist(self): + return self.nodelist + def _gen_nodelist(self): + mylist = self.list + if mylist is None: + mylist = [] + elif not is_Sequence(mylist): + mylist = [mylist] + # The map(self.func) call is what actually turns + # a list into appropriate proxies. + self.nodelist = SCons.Util.NodeList(list(map(self.func, mylist))) + self._create_nodelist = self._return_nodelist + return self.nodelist + _create_nodelist = _gen_nodelist + + +class Targets_or_Sources(collections.UserList): + """A class that implements $TARGETS or $SOURCES expansions by in turn + wrapping a NLWrapper. This class handles the different methods used + to access the list, calling the NLWrapper to create proxies on demand. + + Note that we subclass collections.UserList purely so that the + is_Sequence() function will identify an object of this class as + a list during variable expansion. We're not really using any + collections.UserList methods in practice. + """ + def __init__(self, nl): + self.nl = nl + def __getattr__(self, attr): + nl = self.nl._create_nodelist() + return getattr(nl, attr) + def __getitem__(self, i): + nl = self.nl._create_nodelist() + return nl[i] + def __getslice__(self, i, j): + nl = self.nl._create_nodelist() + i = max(i, 0); j = max(j, 0) + return nl[i:j] + def __str__(self): + nl = self.nl._create_nodelist() + return str(nl) + def __repr__(self): + nl = self.nl._create_nodelist() + return repr(nl) + +class Target_or_Source(object): + """A class that implements $TARGET or $SOURCE expansions by in turn + wrapping a NLWrapper. This class handles the different methods used + to access an individual proxy Node, calling the NLWrapper to create + a proxy on demand. + """ + def __init__(self, nl): + self.nl = nl + def __getattr__(self, attr): + nl = self.nl._create_nodelist() + try: + nl0 = nl[0] + except IndexError: + # If there is nothing in the list, then we have no attributes to + # pass through, so raise AttributeError for everything. + raise AttributeError("NodeList has no attribute: %s" % attr) + return getattr(nl0, attr) + def __str__(self): + nl = self.nl._create_nodelist() + if nl: + return str(nl[0]) + return '' + def __repr__(self): + nl = self.nl._create_nodelist() + if nl: + return repr(nl[0]) + return '' + +class NullNodeList(SCons.Util.NullSeq): + def __call__(self, *args, **kwargs): return '' + def __str__(self): return '' + +NullNodesList = NullNodeList() + +def subst_dict(target, source): + """Create a dictionary for substitution of special + construction variables. + + This translates the following special arguments: + + target - the target (object or array of objects), + used to generate the TARGET and TARGETS + construction variables + + source - the source (object or array of objects), + used to generate the SOURCES and SOURCE + construction variables + """ + dict = {} + + if target: + def get_tgt_subst_proxy(thing): + try: + subst_proxy = thing.get_subst_proxy() + except AttributeError: + subst_proxy = thing # probably a string, just return it + return subst_proxy + tnl = NLWrapper(target, get_tgt_subst_proxy) + dict['TARGETS'] = Targets_or_Sources(tnl) + dict['TARGET'] = Target_or_Source(tnl) + + # This is a total cheat, but hopefully this dictionary goes + # away soon anyway. We just let these expand to $TARGETS + # because that's "good enough" for the use of ToolSurrogates + # (see test/ToolSurrogate.py) to generate documentation. + dict['CHANGED_TARGETS'] = '$TARGETS' + dict['UNCHANGED_TARGETS'] = '$TARGETS' + else: + dict['TARGETS'] = NullNodesList + dict['TARGET'] = NullNodesList + + if source: + def get_src_subst_proxy(node): + try: + rfile = node.rfile + except AttributeError: + pass + else: + node = rfile() + try: + return node.get_subst_proxy() + except AttributeError: + return node # probably a String, just return it + snl = NLWrapper(source, get_src_subst_proxy) + dict['SOURCES'] = Targets_or_Sources(snl) + dict['SOURCE'] = Target_or_Source(snl) + + # This is a total cheat, but hopefully this dictionary goes + # away soon anyway. We just let these expand to $TARGETS + # because that's "good enough" for the use of ToolSurrogates + # (see test/ToolSurrogate.py) to generate documentation. + dict['CHANGED_SOURCES'] = '$SOURCES' + dict['UNCHANGED_SOURCES'] = '$SOURCES' + else: + dict['SOURCES'] = NullNodesList + dict['SOURCE'] = NullNodesList + + return dict + +# Constants for the "mode" parameter to scons_subst_list() and +# scons_subst(). SUBST_RAW gives the raw command line. SUBST_CMD +# gives a command line suitable for passing to a shell. SUBST_SIG +# gives a command line appropriate for calculating the signature +# of a command line...if this changes, we should rebuild. +SUBST_CMD = 0 +SUBST_RAW = 1 +SUBST_SIG = 2 + +_rm = re.compile(r'\$[()]') +_remove = re.compile(r'\$\([^\$]*(\$[^\)][^\$]*)*\$\)') + +# Indexed by the SUBST_* constants above. +_regex_remove = [ _rm, None, _remove ] + +def _rm_list(list): + #return [ l for l in list if not l in ('$(', '$)') ] + return [l for l in list if not l in ('$(', '$)')] + +def _remove_list(list): + result = [] + do_append = result.append + for l in list: + if l == '$(': + do_append = lambda x: None + elif l == '$)': + do_append = result.append + else: + do_append(l) + return result + +# Indexed by the SUBST_* constants above. +_list_remove = [ _rm_list, None, _remove_list ] + +# Regular expressions for splitting strings and handling substitutions, +# for use by the scons_subst() and scons_subst_list() functions: +# +# The first expression compiled matches all of the $-introduced tokens +# that we need to process in some way, and is used for substitutions. +# The expressions it matches are: +# +# "$$" +# "$(" +# "$)" +# "$variable" [must begin with alphabetic or underscore] +# "${any stuff}" +# +# The second expression compiled is used for splitting strings into tokens +# to be processed, and it matches all of the tokens listed above, plus +# the following that affect how arguments do or don't get joined together: +# +# " " [white space] +# "non-white-space" [without any dollar signs] +# "$" [single dollar sign] +# +_dollar_exps_str = r'\$[\$\(\)]|\$[_a-zA-Z][\.\w]*|\${[^}]*}' +_dollar_exps = re.compile(r'(%s)' % _dollar_exps_str) +_separate_args = re.compile(r'(%s|\s+|[^\s\$]+|\$)' % _dollar_exps_str) + +# This regular expression is used to replace strings of multiple white +# space characters in the string result from the scons_subst() function. +_space_sep = re.compile(r'[\t ]+(?![^{]*})') + +def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None): + """Expand a string or list containing construction variable + substitutions. + + This is the work-horse function for substitutions in file names + and the like. The companion scons_subst_list() function (below) + handles separating command lines into lists of arguments, so see + that function if that's what you're looking for. + """ + if isinstance(strSubst, str) and strSubst.find('$') < 0: + return strSubst + + class StringSubber(object): + """A class to construct the results of a scons_subst() call. + + This binds a specific construction environment, mode, target and + source with two methods (substitute() and expand()) that handle + the expansion. + """ + def __init__(self, env, mode, conv, gvars): + self.env = env + self.mode = mode + self.conv = conv + self.gvars = gvars + + def expand(self, s, lvars): + """Expand a single "token" as necessary, returning an + appropriate string containing the expansion. + + This handles expanding different types of things (strings, + lists, callables) appropriately. It calls the wrapper + substitute() method to re-expand things as necessary, so that + the results of expansions of side-by-side strings still get + re-evaluated separately, not smushed together. + """ + if is_String(s): + try: + s0, s1 = s[:2] + except (IndexError, ValueError): + return s + if s0 != '$': + return s + if s1 == '$': + return '$' + elif s1 in '()': + return s + else: + key = s[1:] + if key[0] == '{' or key.find('.') >= 0: + if key[0] == '{': + key = key[1:-1] + try: + s = eval(key, self.gvars, lvars) + except KeyboardInterrupt: + raise + except Exception, e: + if e.__class__ in AllowableExceptions: + return '' + raise_exception(e, lvars['TARGETS'], s) + else: + if key in lvars: + s = lvars[key] + elif key in self.gvars: + s = self.gvars[key] + elif not NameError in AllowableExceptions: + raise_exception(NameError(key), lvars['TARGETS'], s) + else: + return '' + + # Before re-expanding the result, handle + # recursive expansion by copying the local + # variable dictionary and overwriting a null + # string for the value of the variable name + # we just expanded. + # + # This could potentially be optimized by only + # copying lvars when s contains more expansions, + # but lvars is usually supposed to be pretty + # small, and deeply nested variable expansions + # are probably more the exception than the norm, + # so it should be tolerable for now. + lv = lvars.copy() + var = key.split('.')[0] + lv[var] = '' + return self.substitute(s, lv) + elif is_Sequence(s): + def func(l, conv=self.conv, substitute=self.substitute, lvars=lvars): + return conv(substitute(l, lvars)) + return list(map(func, s)) + elif callable(s): + try: + s = s(target=lvars['TARGETS'], + source=lvars['SOURCES'], + env=self.env, + for_signature=(self.mode != SUBST_CMD)) + except TypeError: + # This probably indicates that it's a callable + # object that doesn't match our calling arguments + # (like an Action). + if self.mode == SUBST_RAW: + return s + s = self.conv(s) + return self.substitute(s, lvars) + elif s is None: + return '' + else: + return s + + def substitute(self, args, lvars): + """Substitute expansions in an argument or list of arguments. + + This serves as a wrapper for splitting up a string into + separate tokens. + """ + if is_String(args) and not isinstance(args, CmdStringHolder): + args = str(args) # In case it's a UserString. + try: + def sub_match(match): + return self.conv(self.expand(match.group(1), lvars)) + result = _dollar_exps.sub(sub_match, args) + except TypeError: + # If the internal conversion routine doesn't return + # strings (it could be overridden to return Nodes, for + # example), then the 1.5.2 re module will throw this + # exception. Back off to a slower, general-purpose + # algorithm that works for all data types. + args = _separate_args.findall(args) + result = [] + for a in args: + result.append(self.conv(self.expand(a, lvars))) + if len(result) == 1: + result = result[0] + else: + result = ''.join(map(str, result)) + return result + else: + return self.expand(args, lvars) + + if conv is None: + conv = _strconv[mode] + + # Doing this every time is a bit of a waste, since the Executor + # has typically already populated the OverrideEnvironment with + # $TARGET/$SOURCE variables. We're keeping this (for now), though, + # because it supports existing behavior that allows us to call + # an Action directly with an arbitrary target+source pair, which + # we use in Tool/tex.py to handle calling $BIBTEX when necessary. + # If we dropped that behavior (or found another way to cover it), + # we could get rid of this call completely and just rely on the + # Executor setting the variables. + if 'TARGET' not in lvars: + d = subst_dict(target, source) + if d: + lvars = lvars.copy() + lvars.update(d) + + # We're (most likely) going to eval() things. If Python doesn't + # find a __builtins__ value in the global dictionary used for eval(), + # it copies the current global values for you. Avoid this by + # setting it explicitly and then deleting, so we don't pollute the + # construction environment Dictionary(ies) that are typically used + # for expansion. + gvars['__builtins__'] = __builtins__ + + ss = StringSubber(env, mode, conv, gvars) + result = ss.substitute(strSubst, lvars) + + try: + del gvars['__builtins__'] + except KeyError: + pass + + if is_String(result): + # Remove $(-$) pairs and any stuff in between, + # if that's appropriate. + remove = _regex_remove[mode] + if remove: + result = remove.sub('', result) + if mode != SUBST_RAW: + # Compress strings of white space characters into + # a single space. + result = _space_sep.sub(' ', result).strip() + elif is_Sequence(result): + remove = _list_remove[mode] + if remove: + result = remove(result) + + return result + +#Subst_List_Strings = {} + +def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None): + """Substitute construction variables in a string (or list or other + object) and separate the arguments into a command list. + + The companion scons_subst() function (above) handles basic + substitutions within strings, so see that function instead + if that's what you're looking for. + """ +# try: +# Subst_List_Strings[strSubst] = Subst_List_Strings[strSubst] + 1 +# except KeyError: +# Subst_List_Strings[strSubst] = 1 +# import SCons.Debug +# SCons.Debug.caller_trace(1) + class ListSubber(collections.UserList): + """A class to construct the results of a scons_subst_list() call. + + Like StringSubber, this class binds a specific construction + environment, mode, target and source with two methods + (substitute() and expand()) that handle the expansion. + + In addition, however, this class is used to track the state of + the result(s) we're gathering so we can do the appropriate thing + whenever we have to append another word to the result--start a new + line, start a new word, append to the current word, etc. We do + this by setting the "append" attribute to the right method so + that our wrapper methods only need ever call ListSubber.append(), + and the rest of the object takes care of doing the right thing + internally. + """ + def __init__(self, env, mode, conv, gvars): + collections.UserList.__init__(self, []) + self.env = env + self.mode = mode + self.conv = conv + self.gvars = gvars + + if self.mode == SUBST_RAW: + self.add_strip = lambda x: self.append(x) + else: + self.add_strip = lambda x: None + self.in_strip = None + self.next_line() + + def expand(self, s, lvars, within_list): + """Expand a single "token" as necessary, appending the + expansion to the current result. + + This handles expanding different types of things (strings, + lists, callables) appropriately. It calls the wrapper + substitute() method to re-expand things as necessary, so that + the results of expansions of side-by-side strings still get + re-evaluated separately, not smushed together. + """ + + if is_String(s): + try: + s0, s1 = s[:2] + except (IndexError, ValueError): + self.append(s) + return + if s0 != '$': + self.append(s) + return + if s1 == '$': + self.append('$') + elif s1 == '(': + self.open_strip('$(') + elif s1 == ')': + self.close_strip('$)') + else: + key = s[1:] + if key[0] == '{' or key.find('.') >= 0: + if key[0] == '{': + key = key[1:-1] + try: + s = eval(key, self.gvars, lvars) + except KeyboardInterrupt: + raise + except Exception, e: + if e.__class__ in AllowableExceptions: + return + raise_exception(e, lvars['TARGETS'], s) + else: + if key in lvars: + s = lvars[key] + elif key in self.gvars: + s = self.gvars[key] + elif not NameError in AllowableExceptions: + raise_exception(NameError(), lvars['TARGETS'], s) + else: + return + + # Before re-expanding the result, handle + # recursive expansion by copying the local + # variable dictionary and overwriting a null + # string for the value of the variable name + # we just expanded. + lv = lvars.copy() + var = key.split('.')[0] + lv[var] = '' + self.substitute(s, lv, 0) + self.this_word() + elif is_Sequence(s): + for a in s: + self.substitute(a, lvars, 1) + self.next_word() + elif callable(s): + try: + s = s(target=lvars['TARGETS'], + source=lvars['SOURCES'], + env=self.env, + for_signature=(self.mode != SUBST_CMD)) + except TypeError: + # This probably indicates that it's a callable + # object that doesn't match our calling arguments + # (like an Action). + if self.mode == SUBST_RAW: + self.append(s) + return + s = self.conv(s) + self.substitute(s, lvars, within_list) + elif s is None: + self.this_word() + else: + self.append(s) + + def substitute(self, args, lvars, within_list): + """Substitute expansions in an argument or list of arguments. + + This serves as a wrapper for splitting up a string into + separate tokens. + """ + + if is_String(args) and not isinstance(args, CmdStringHolder): + args = str(args) # In case it's a UserString. + args = _separate_args.findall(args) + for a in args: + if a[0] in ' \t\n\r\f\v': + if '\n' in a: + self.next_line() + elif within_list: + self.append(a) + else: + self.next_word() + else: + self.expand(a, lvars, within_list) + else: + self.expand(args, lvars, within_list) + + def next_line(self): + """Arrange for the next word to start a new line. This + is like starting a new word, except that we have to append + another line to the result.""" + collections.UserList.append(self, []) + self.next_word() + + def this_word(self): + """Arrange for the next word to append to the end of the + current last word in the result.""" + self.append = self.add_to_current_word + + def next_word(self): + """Arrange for the next word to start a new word.""" + self.append = self.add_new_word + + def add_to_current_word(self, x): + """Append the string x to the end of the current last word + in the result. If that is not possible, then just add + it as a new word. Make sure the entire concatenated string + inherits the object attributes of x (in particular, the + escape function) by wrapping it as CmdStringHolder.""" + + if not self.in_strip or self.mode != SUBST_SIG: + try: + current_word = self[-1][-1] + except IndexError: + self.add_new_word(x) + else: + # All right, this is a hack and it should probably + # be refactored out of existence in the future. + # The issue is that we want to smoosh words together + # and make one file name that gets escaped if + # we're expanding something like foo$EXTENSION, + # but we don't want to smoosh them together if + # it's something like >$TARGET, because then we'll + # treat the '>' like it's part of the file name. + # So for now, just hard-code looking for the special + # command-line redirection characters... + try: + last_char = str(current_word)[-1] + except IndexError: + last_char = '\0' + if last_char in '<>|': + self.add_new_word(x) + else: + y = current_word + x + + # We used to treat a word appended to a literal + # as a literal itself, but this caused problems + # with interpreting quotes around space-separated + # targets on command lines. Removing this makes + # none of the "substantive" end-to-end tests fail, + # so we'll take this out but leave it commented + # for now in case there's a problem not covered + # by the test cases and we need to resurrect this. + #literal1 = self.literal(self[-1][-1]) + #literal2 = self.literal(x) + y = self.conv(y) + if is_String(y): + #y = CmdStringHolder(y, literal1 or literal2) + y = CmdStringHolder(y, None) + self[-1][-1] = y + + def add_new_word(self, x): + if not self.in_strip or self.mode != SUBST_SIG: + literal = self.literal(x) + x = self.conv(x) + if is_String(x): + x = CmdStringHolder(x, literal) + self[-1].append(x) + self.append = self.add_to_current_word + + def literal(self, x): + try: + l = x.is_literal + except AttributeError: + return None + else: + return l() + + def open_strip(self, x): + """Handle the "open strip" $( token.""" + self.add_strip(x) + self.in_strip = 1 + + def close_strip(self, x): + """Handle the "close strip" $) token.""" + self.add_strip(x) + self.in_strip = None + + if conv is None: + conv = _strconv[mode] + + # Doing this every time is a bit of a waste, since the Executor + # has typically already populated the OverrideEnvironment with + # $TARGET/$SOURCE variables. We're keeping this (for now), though, + # because it supports existing behavior that allows us to call + # an Action directly with an arbitrary target+source pair, which + # we use in Tool/tex.py to handle calling $BIBTEX when necessary. + # If we dropped that behavior (or found another way to cover it), + # we could get rid of this call completely and just rely on the + # Executor setting the variables. + if 'TARGET' not in lvars: + d = subst_dict(target, source) + if d: + lvars = lvars.copy() + lvars.update(d) + + # We're (most likely) going to eval() things. If Python doesn't + # find a __builtins__ value in the global dictionary used for eval(), + # it copies the current global values for you. Avoid this by + # setting it explicitly and then deleting, so we don't pollute the + # construction environment Dictionary(ies) that are typically used + # for expansion. + gvars['__builtins__'] = __builtins__ + + ls = ListSubber(env, mode, conv, gvars) + ls.substitute(strSubst, lvars, 0) + + try: + del gvars['__builtins__'] + except KeyError: + pass + + return ls.data + +def scons_subst_once(strSubst, env, key): + """Perform single (non-recursive) substitution of a single + construction variable keyword. + + This is used when setting a variable when copying or overriding values + in an Environment. We want to capture (expand) the old value before + we override it, so people can do things like: + + env2 = env.Clone(CCFLAGS = '$CCFLAGS -g') + + We do this with some straightforward, brute-force code here... + """ + if isinstance(strSubst, str) and strSubst.find('$') < 0: + return strSubst + + matchlist = ['$' + key, '${' + key + '}'] + val = env.get(key, '') + def sub_match(match, val=val, matchlist=matchlist): + a = match.group(1) + if a in matchlist: + a = val + if is_Sequence(a): + return ' '.join(map(str, a)) + else: + return str(a) + + if is_Sequence(strSubst): + result = [] + for arg in strSubst: + if is_String(arg): + if arg in matchlist: + arg = val + if is_Sequence(arg): + result.extend(arg) + else: + result.append(arg) + else: + result.append(_dollar_exps.sub(sub_match, arg)) + else: + result.append(arg) + return result + elif is_String(strSubst): + return _dollar_exps.sub(sub_match, strSubst) + else: + return strSubst + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Taskmaster.py b/scons/scons-local-2.3.0/SCons/Taskmaster.py new file mode 100644 index 000000000..461023a01 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Taskmaster.py @@ -0,0 +1,1032 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__doc__ = """ +Generic Taskmaster module for the SCons build engine. + +This module contains the primary interface(s) between a wrapping user +interface and the SCons build engine. There are two key classes here: + + Taskmaster + This is the main engine for walking the dependency graph and + calling things to decide what does or doesn't need to be built. + + Task + This is the base class for allowing a wrapping interface to + decide what does or doesn't actually need to be done. The + intention is for a wrapping interface to subclass this as + appropriate for different types of behavior it may need. + + The canonical example is the SCons native Python interface, + which has Task subclasses that handle its specific behavior, + like printing "`foo' is up to date" when a top-level target + doesn't need to be built, and handling the -c option by removing + targets as its "build" action. There is also a separate subclass + for suppressing this output when the -q option is used. + + The Taskmaster instantiates a Task object for each (set of) + target(s) that it decides need to be evaluated and/or built. +""" + +__revision__ = "src/engine/SCons/Taskmaster.py 2013/03/03 09:48:35 garyo" + +from itertools import chain +import operator +import sys +import traceback + +import SCons.Errors +import SCons.Node +import SCons.Warnings + +StateString = SCons.Node.StateString +NODE_NO_STATE = SCons.Node.no_state +NODE_PENDING = SCons.Node.pending +NODE_EXECUTING = SCons.Node.executing +NODE_UP_TO_DATE = SCons.Node.up_to_date +NODE_EXECUTED = SCons.Node.executed +NODE_FAILED = SCons.Node.failed + +print_prepare = 0 # set by option --debug=prepare + +# A subsystem for recording stats about how different Nodes are handled by +# the main Taskmaster loop. There's no external control here (no need for +# a --debug= option); enable it by changing the value of CollectStats. + +CollectStats = None + +class Stats(object): + """ + A simple class for holding statistics about the disposition of a + Node by the Taskmaster. If we're collecting statistics, each Node + processed by the Taskmaster gets one of these attached, in which case + the Taskmaster records its decision each time it processes the Node. + (Ideally, that's just once per Node.) + """ + def __init__(self): + """ + Instantiates a Taskmaster.Stats object, initializing all + appropriate counters to zero. + """ + self.considered = 0 + self.already_handled = 0 + self.problem = 0 + self.child_failed = 0 + self.not_built = 0 + self.side_effects = 0 + self.build = 0 + +StatsNodes = [] + +fmt = "%(considered)3d "\ + "%(already_handled)3d " \ + "%(problem)3d " \ + "%(child_failed)3d " \ + "%(not_built)3d " \ + "%(side_effects)3d " \ + "%(build)3d " + +def dump_stats(): + for n in sorted(StatsNodes, key=lambda a: str(a)): + print (fmt % n.stats.__dict__) + str(n) + + + +class Task(object): + """ + Default SCons build engine task. + + This controls the interaction of the actual building of node + and the rest of the engine. + + This is expected to handle all of the normally-customizable + aspects of controlling a build, so any given application + *should* be able to do what it wants by sub-classing this + class and overriding methods as appropriate. If an application + needs to customze something by sub-classing Taskmaster (or + some other build engine class), we should first try to migrate + that functionality into this class. + + Note that it's generally a good idea for sub-classes to call + these methods explicitly to update state, etc., rather than + roll their own interaction with Taskmaster from scratch. + """ + def __init__(self, tm, targets, top, node): + self.tm = tm + self.targets = targets + self.top = top + self.node = node + self.exc_clear() + + def trace_message(self, method, node, description='node'): + fmt = '%-20s %s %s\n' + return fmt % (method + ':', description, self.tm.trace_node(node)) + + def display(self, message): + """ + Hook to allow the calling interface to display a message. + + This hook gets called as part of preparing a task for execution + (that is, a Node to be built). As part of figuring out what Node + should be built next, the actually target list may be altered, + along with a message describing the alteration. The calling + interface can subclass Task and provide a concrete implementation + of this method to see those messages. + """ + pass + + def prepare(self): + """ + Called just before the task is executed. + + This is mainly intended to give the target Nodes a chance to + unlink underlying files and make all necessary directories before + the Action is actually called to build the targets. + """ + global print_prepare + T = self.tm.trace + if T: T.write(self.trace_message(u'Task.prepare()', self.node)) + + # Now that it's the appropriate time, give the TaskMaster a + # chance to raise any exceptions it encountered while preparing + # this task. + self.exception_raise() + + if self.tm.message: + self.display(self.tm.message) + self.tm.message = None + + # Let the targets take care of any necessary preparations. + # This includes verifying that all of the necessary sources + # and dependencies exist, removing the target file(s), etc. + # + # As of April 2008, the get_executor().prepare() method makes + # sure that all of the aggregate sources necessary to build this + # Task's target(s) exist in one up-front check. The individual + # target t.prepare() methods check that each target's explicit + # or implicit dependencies exists, and also initialize the + # .sconsign info. + executor = self.targets[0].get_executor() + executor.prepare() + for t in executor.get_action_targets(): + if print_prepare: + print "Preparing target %s..."%t + for s in t.side_effects: + print "...with side-effect %s..."%s + t.prepare() + for s in t.side_effects: + if print_prepare: + print "...Preparing side-effect %s..."%s + s.prepare() + + def get_target(self): + """Fetch the target being built or updated by this task. + """ + return self.node + + def needs_execute(self): + # TODO(deprecate): "return True" is the old default behavior; + # change it to NotImplementedError (after running through the + # Deprecation Cycle) so the desired behavior is explicitly + # determined by which concrete subclass is used. + #raise NotImplementedError + msg = ('Taskmaster.Task is an abstract base class; instead of\n' + '\tusing it directly, ' + 'derive from it and override the abstract methods.') + SCons.Warnings.warn(SCons.Warnings.TaskmasterNeedsExecuteWarning, msg) + return True + + def execute(self): + """ + Called to execute the task. + + This method is called from multiple threads in a parallel build, + so only do thread safe stuff here. Do thread unsafe stuff in + prepare(), executed() or failed(). + """ + T = self.tm.trace + if T: T.write(self.trace_message(u'Task.execute()', self.node)) + + try: + cached_targets = [] + for t in self.targets: + if not t.retrieve_from_cache(): + break + cached_targets.append(t) + if len(cached_targets) < len(self.targets): + # Remove targets before building. It's possible that we + # partially retrieved targets from the cache, leaving + # them in read-only mode. That might cause the command + # to fail. + # + for t in cached_targets: + try: + t.fs.unlink(t.path) + except (IOError, OSError): + pass + self.targets[0].build() + else: + for t in cached_targets: + t.cached = 1 + except SystemExit: + exc_value = sys.exc_info()[1] + raise SCons.Errors.ExplicitExit(self.targets[0], exc_value.code) + except SCons.Errors.UserError: + raise + except SCons.Errors.BuildError: + raise + except Exception, e: + buildError = SCons.Errors.convert_to_BuildError(e) + buildError.node = self.targets[0] + buildError.exc_info = sys.exc_info() + raise buildError + + def executed_without_callbacks(self): + """ + Called when the task has been successfully executed + and the Taskmaster instance doesn't want to call + the Node's callback methods. + """ + T = self.tm.trace + if T: T.write(self.trace_message('Task.executed_without_callbacks()', + self.node)) + + for t in self.targets: + if t.get_state() == NODE_EXECUTING: + for side_effect in t.side_effects: + side_effect.set_state(NODE_NO_STATE) + t.set_state(NODE_EXECUTED) + + def executed_with_callbacks(self): + """ + Called when the task has been successfully executed and + the Taskmaster instance wants to call the Node's callback + methods. + + This may have been a do-nothing operation (to preserve build + order), so we must check the node's state before deciding whether + it was "built", in which case we call the appropriate Node method. + In any event, we always call "visited()", which will handle any + post-visit actions that must take place regardless of whether + or not the target was an actual built target or a source Node. + """ + T = self.tm.trace + if T: T.write(self.trace_message('Task.executed_with_callbacks()', + self.node)) + + for t in self.targets: + if t.get_state() == NODE_EXECUTING: + for side_effect in t.side_effects: + side_effect.set_state(NODE_NO_STATE) + t.set_state(NODE_EXECUTED) + if not t.cached: + t.push_to_cache() + t.built() + t.visited() + + executed = executed_with_callbacks + + def failed(self): + """ + Default action when a task fails: stop the build. + + Note: Although this function is normally invoked on nodes in + the executing state, it might also be invoked on up-to-date + nodes when using Configure(). + """ + self.fail_stop() + + def fail_stop(self): + """ + Explicit stop-the-build failure. + + This sets failure status on the target nodes and all of + their dependent parent nodes. + + Note: Although this function is normally invoked on nodes in + the executing state, it might also be invoked on up-to-date + nodes when using Configure(). + """ + T = self.tm.trace + if T: T.write(self.trace_message('Task.failed_stop()', self.node)) + + # Invoke will_not_build() to clean-up the pending children + # list. + self.tm.will_not_build(self.targets, lambda n: n.set_state(NODE_FAILED)) + + # Tell the taskmaster to not start any new tasks + self.tm.stop() + + # We're stopping because of a build failure, but give the + # calling Task class a chance to postprocess() the top-level + # target under which the build failure occurred. + self.targets = [self.tm.current_top] + self.top = 1 + + def fail_continue(self): + """ + Explicit continue-the-build failure. + + This sets failure status on the target nodes and all of + their dependent parent nodes. + + Note: Although this function is normally invoked on nodes in + the executing state, it might also be invoked on up-to-date + nodes when using Configure(). + """ + T = self.tm.trace + if T: T.write(self.trace_message('Task.failed_continue()', self.node)) + + self.tm.will_not_build(self.targets, lambda n: n.set_state(NODE_FAILED)) + + def make_ready_all(self): + """ + Marks all targets in a task ready for execution. + + This is used when the interface needs every target Node to be + visited--the canonical example being the "scons -c" option. + """ + T = self.tm.trace + if T: T.write(self.trace_message('Task.make_ready_all()', self.node)) + + self.out_of_date = self.targets[:] + for t in self.targets: + t.disambiguate().set_state(NODE_EXECUTING) + for s in t.side_effects: + # add disambiguate here to mirror the call on targets above + s.disambiguate().set_state(NODE_EXECUTING) + + def make_ready_current(self): + """ + Marks all targets in a task ready for execution if any target + is not current. + + This is the default behavior for building only what's necessary. + """ + T = self.tm.trace + if T: T.write(self.trace_message(u'Task.make_ready_current()', + self.node)) + + self.out_of_date = [] + needs_executing = False + for t in self.targets: + try: + t.disambiguate().make_ready() + is_up_to_date = not t.has_builder() or \ + (not t.always_build and t.is_up_to_date()) + except EnvironmentError, e: + raise SCons.Errors.BuildError(node=t, errstr=e.strerror, filename=e.filename) + + if not is_up_to_date: + self.out_of_date.append(t) + needs_executing = True + + if needs_executing: + for t in self.targets: + t.set_state(NODE_EXECUTING) + for s in t.side_effects: + # add disambiguate here to mirror the call on targets in first loop above + s.disambiguate().set_state(NODE_EXECUTING) + else: + for t in self.targets: + # We must invoke visited() to ensure that the node + # information has been computed before allowing the + # parent nodes to execute. (That could occur in a + # parallel build...) + t.visited() + t.set_state(NODE_UP_TO_DATE) + + make_ready = make_ready_current + + def postprocess(self): + """ + Post-processes a task after it's been executed. + + This examines all the targets just built (or not, we don't care + if the build was successful, or even if there was no build + because everything was up-to-date) to see if they have any + waiting parent Nodes, or Nodes waiting on a common side effect, + that can be put back on the candidates list. + """ + T = self.tm.trace + if T: T.write(self.trace_message(u'Task.postprocess()', self.node)) + + # We may have built multiple targets, some of which may have + # common parents waiting for this build. Count up how many + # targets each parent was waiting for so we can subtract the + # values later, and so we *don't* put waiting side-effect Nodes + # back on the candidates list if the Node is also a waiting + # parent. + + targets = set(self.targets) + + pending_children = self.tm.pending_children + parents = {} + for t in targets: + # A node can only be in the pending_children set if it has + # some waiting_parents. + if t.waiting_parents: + if T: T.write(self.trace_message(u'Task.postprocess()', + t, + 'removing')) + pending_children.discard(t) + for p in t.waiting_parents: + parents[p] = parents.get(p, 0) + 1 + + for t in targets: + for s in t.side_effects: + if s.get_state() == NODE_EXECUTING: + s.set_state(NODE_NO_STATE) + for p in s.waiting_parents: + parents[p] = parents.get(p, 0) + 1 + for p in s.waiting_s_e: + if p.ref_count == 0: + self.tm.candidates.append(p) + + for p, subtract in parents.items(): + p.ref_count = p.ref_count - subtract + if T: T.write(self.trace_message(u'Task.postprocess()', + p, + 'adjusted parent ref count')) + if p.ref_count == 0: + self.tm.candidates.append(p) + + for t in targets: + t.postprocess() + + # Exception handling subsystem. + # + # Exceptions that occur while walking the DAG or examining Nodes + # must be raised, but must be raised at an appropriate time and in + # a controlled manner so we can, if necessary, recover gracefully, + # possibly write out signature information for Nodes we've updated, + # etc. This is done by having the Taskmaster tell us about the + # exception, and letting + + def exc_info(self): + """ + Returns info about a recorded exception. + """ + return self.exception + + def exc_clear(self): + """ + Clears any recorded exception. + + This also changes the "exception_raise" attribute to point + to the appropriate do-nothing method. + """ + self.exception = (None, None, None) + self.exception_raise = self._no_exception_to_raise + + def exception_set(self, exception=None): + """ + Records an exception to be raised at the appropriate time. + + This also changes the "exception_raise" attribute to point + to the method that will, in fact + """ + if not exception: + exception = sys.exc_info() + self.exception = exception + self.exception_raise = self._exception_raise + + def _no_exception_to_raise(self): + pass + + def _exception_raise(self): + """ + Raises a pending exception that was recorded while getting a + Task ready for execution. + """ + exc = self.exc_info()[:] + try: + exc_type, exc_value, exc_traceback = exc + except ValueError: + exc_type, exc_value = exc + exc_traceback = None + raise exc_type, exc_value, exc_traceback + +class AlwaysTask(Task): + def needs_execute(self): + """ + Always returns True (indicating this Task should always + be executed). + + Subclasses that need this behavior (as opposed to the default + of only executing Nodes that are out of date w.r.t. their + dependencies) can use this as follows: + + class MyTaskSubclass(SCons.Taskmaster.Task): + needs_execute = SCons.Taskmaster.Task.execute_always + """ + return True + +class OutOfDateTask(Task): + def needs_execute(self): + """ + Returns True (indicating this Task should be executed) if this + Task's target state indicates it needs executing, which has + already been determined by an earlier up-to-date check. + """ + return self.targets[0].get_state() == SCons.Node.executing + + +def find_cycle(stack, visited): + if stack[-1] in visited: + return None + visited.add(stack[-1]) + for n in stack[-1].waiting_parents: + stack.append(n) + if stack[0] == stack[-1]: + return stack + if find_cycle(stack, visited): + return stack + stack.pop() + return None + + +class Taskmaster(object): + """ + The Taskmaster for walking the dependency DAG. + """ + + def __init__(self, targets=[], tasker=None, order=None, trace=None): + self.original_top = targets + self.top_targets_left = targets[:] + self.top_targets_left.reverse() + self.candidates = [] + if tasker is None: + tasker = OutOfDateTask + self.tasker = tasker + if not order: + order = lambda l: l + self.order = order + self.message = None + self.trace = trace + self.next_candidate = self.find_next_candidate + self.pending_children = set() + + def find_next_candidate(self): + """ + Returns the next candidate Node for (potential) evaluation. + + The candidate list (really a stack) initially consists of all of + the top-level (command line) targets provided when the Taskmaster + was initialized. While we walk the DAG, visiting Nodes, all the + children that haven't finished processing get pushed on to the + candidate list. Each child can then be popped and examined in + turn for whether *their* children are all up-to-date, in which + case a Task will be created for their actual evaluation and + potential building. + + Here is where we also allow candidate Nodes to alter the list of + Nodes that should be examined. This is used, for example, when + invoking SCons in a source directory. A source directory Node can + return its corresponding build directory Node, essentially saying, + "Hey, you really need to build this thing over here instead." + """ + try: + return self.candidates.pop() + except IndexError: + pass + try: + node = self.top_targets_left.pop() + except IndexError: + return None + self.current_top = node + alt, message = node.alter_targets() + if alt: + self.message = message + self.candidates.append(node) + self.candidates.extend(self.order(alt)) + node = self.candidates.pop() + return node + + def no_next_candidate(self): + """ + Stops Taskmaster processing by not returning a next candidate. + + Note that we have to clean-up the Taskmaster candidate list + because the cycle detection depends on the fact all nodes have + been processed somehow. + """ + while self.candidates: + candidates = self.candidates + self.candidates = [] + self.will_not_build(candidates) + return None + + def _validate_pending_children(self): + """ + Validate the content of the pending_children set. Assert if an + internal error is found. + + This function is used strictly for debugging the taskmaster by + checking that no invariants are violated. It is not used in + normal operation. + + The pending_children set is used to detect cycles in the + dependency graph. We call a "pending child" a child that is + found in the "pending" state when checking the dependencies of + its parent node. + + A pending child can occur when the Taskmaster completes a loop + through a cycle. For example, lets imagine a graph made of + three node (A, B and C) making a cycle. The evaluation starts + at node A. The taskmaster first consider whether node A's + child B is up-to-date. Then, recursively, node B needs to + check whether node C is up-to-date. This leaves us with a + dependency graph looking like: + + Next candidate \ + \ + Node A (Pending) --> Node B(Pending) --> Node C (NoState) + ^ | + | | + +-------------------------------------+ + + Now, when the Taskmaster examines the Node C's child Node A, + it finds that Node A is in the "pending" state. Therefore, + Node A is a pending child of node C. + + Pending children indicate that the Taskmaster has potentially + loop back through a cycle. We say potentially because it could + also occur when a DAG is evaluated in parallel. For example, + consider the following graph: + + + Node A (Pending) --> Node B(Pending) --> Node C (Pending) --> ... + | ^ + | | + +----------> Node D (NoState) --------+ + / + Next candidate / + + The Taskmaster first evaluates the nodes A, B, and C and + starts building some children of node C. Assuming, that the + maximum parallel level has not been reached, the Taskmaster + will examine Node D. It will find that Node C is a pending + child of Node D. + + In summary, evaluating a graph with a cycle will always + involve a pending child at one point. A pending child might + indicate either a cycle or a diamond-shaped DAG. Only a + fraction of the nodes ends-up being a "pending child" of + another node. This keeps the pending_children set small in + practice. + + We can differentiate between the two cases if we wait until + the end of the build. At this point, all the pending children + nodes due to a diamond-shaped DAG will have been properly + built (or will have failed to build). But, the pending + children involved in a cycle will still be in the pending + state. + + The taskmaster removes nodes from the pending_children set as + soon as a pending_children node moves out of the pending + state. This also helps to keep the pending_children set small. + """ + + for n in self.pending_children: + assert n.state in (NODE_PENDING, NODE_EXECUTING), \ + (str(n), StateString[n.state]) + assert len(n.waiting_parents) != 0, (str(n), len(n.waiting_parents)) + for p in n.waiting_parents: + assert p.ref_count > 0, (str(n), str(p), p.ref_count) + + + def trace_message(self, message): + return 'Taskmaster: %s\n' % message + + def trace_node(self, node): + return '<%-10s %-3s %s>' % (StateString[node.get_state()], + node.ref_count, + repr(str(node))) + + def _find_next_ready_node(self): + """ + Finds the next node that is ready to be built. + + This is *the* main guts of the DAG walk. We loop through the + list of candidates, looking for something that has no un-built + children (i.e., that is a leaf Node or has dependencies that are + all leaf Nodes or up-to-date). Candidate Nodes are re-scanned + (both the target Node itself and its sources, which are always + scanned in the context of a given target) to discover implicit + dependencies. A Node that must wait for some children to be + built will be put back on the candidates list after the children + have finished building. A Node that has been put back on the + candidates list in this way may have itself (or its sources) + re-scanned, in order to handle generated header files (e.g.) and + the implicit dependencies therein. + + Note that this method does not do any signature calculation or + up-to-date check itself. All of that is handled by the Task + class. This is purely concerned with the dependency graph walk. + """ + + self.ready_exc = None + + T = self.trace + if T: T.write(u'\n' + self.trace_message('Looking for a node to evaluate')) + + while True: + node = self.next_candidate() + if node is None: + if T: T.write(self.trace_message('No candidate anymore.') + u'\n') + return None + + node = node.disambiguate() + state = node.get_state() + + # For debugging only: + # + # try: + # self._validate_pending_children() + # except: + # self.ready_exc = sys.exc_info() + # return node + + if CollectStats: + if not hasattr(node, 'stats'): + node.stats = Stats() + StatsNodes.append(node) + S = node.stats + S.considered = S.considered + 1 + else: + S = None + + if T: T.write(self.trace_message(u' Considering node %s and its children:' % self.trace_node(node))) + + if state == NODE_NO_STATE: + # Mark this node as being on the execution stack: + node.set_state(NODE_PENDING) + elif state > NODE_PENDING: + # Skip this node if it has already been evaluated: + if S: S.already_handled = S.already_handled + 1 + if T: T.write(self.trace_message(u' already handled (executed)')) + continue + + executor = node.get_executor() + + try: + children = executor.get_all_children() + except SystemExit: + exc_value = sys.exc_info()[1] + e = SCons.Errors.ExplicitExit(node, exc_value.code) + self.ready_exc = (SCons.Errors.ExplicitExit, e) + if T: T.write(self.trace_message(' SystemExit')) + return node + except Exception, e: + # We had a problem just trying to figure out the + # children (like a child couldn't be linked in to a + # VariantDir, or a Scanner threw something). Arrange to + # raise the exception when the Task is "executed." + self.ready_exc = sys.exc_info() + if S: S.problem = S.problem + 1 + if T: T.write(self.trace_message(' exception %s while scanning children.\n' % e)) + return node + + children_not_visited = [] + children_pending = set() + children_not_ready = [] + children_failed = False + + for child in chain(executor.get_all_prerequisites(), children): + childstate = child.get_state() + + if T: T.write(self.trace_message(u' ' + self.trace_node(child))) + + if childstate == NODE_NO_STATE: + children_not_visited.append(child) + elif childstate == NODE_PENDING: + children_pending.add(child) + elif childstate == NODE_FAILED: + children_failed = True + + if childstate <= NODE_EXECUTING: + children_not_ready.append(child) + + + # These nodes have not even been visited yet. Add + # them to the list so that on some next pass we can + # take a stab at evaluating them (or their children). + children_not_visited.reverse() + self.candidates.extend(self.order(children_not_visited)) + #if T and children_not_visited: + # T.write(self.trace_message(' adding to candidates: %s' % map(str, children_not_visited))) + # T.write(self.trace_message(' candidates now: %s\n' % map(str, self.candidates))) + + # Skip this node if any of its children have failed. + # + # This catches the case where we're descending a top-level + # target and one of our children failed while trying to be + # built by a *previous* descent of an earlier top-level + # target. + # + # It can also occur if a node is reused in multiple + # targets. One first descends though the one of the + # target, the next time occurs through the other target. + # + # Note that we can only have failed_children if the + # --keep-going flag was used, because without it the build + # will stop before diving in the other branch. + # + # Note that even if one of the children fails, we still + # added the other children to the list of candidate nodes + # to keep on building (--keep-going). + if children_failed: + for n in executor.get_action_targets(): + n.set_state(NODE_FAILED) + + if S: S.child_failed = S.child_failed + 1 + if T: T.write(self.trace_message('****** %s\n' % self.trace_node(node))) + continue + + if children_not_ready: + for child in children_not_ready: + # We're waiting on one or more derived targets + # that have not yet finished building. + if S: S.not_built = S.not_built + 1 + + # Add this node to the waiting parents lists of + # anything we're waiting on, with a reference + # count so we can be put back on the list for + # re-evaluation when they've all finished. + node.ref_count = node.ref_count + child.add_to_waiting_parents(node) + if T: T.write(self.trace_message(u' adjusted ref count: %s, child %s' % + (self.trace_node(node), repr(str(child))))) + + if T: + for pc in children_pending: + T.write(self.trace_message(' adding %s to the pending children set\n' % + self.trace_node(pc))) + self.pending_children = self.pending_children | children_pending + + continue + + # Skip this node if it has side-effects that are + # currently being built: + wait_side_effects = False + for se in executor.get_action_side_effects(): + if se.get_state() == NODE_EXECUTING: + se.add_to_waiting_s_e(node) + wait_side_effects = True + + if wait_side_effects: + if S: S.side_effects = S.side_effects + 1 + continue + + # The default when we've gotten through all of the checks above: + # this node is ready to be built. + if S: S.build = S.build + 1 + if T: T.write(self.trace_message(u'Evaluating %s\n' % + self.trace_node(node))) + + # For debugging only: + # + # try: + # self._validate_pending_children() + # except: + # self.ready_exc = sys.exc_info() + # return node + + return node + + return None + + def next_task(self): + """ + Returns the next task to be executed. + + This simply asks for the next Node to be evaluated, and then wraps + it in the specific Task subclass with which we were initialized. + """ + node = self._find_next_ready_node() + + if node is None: + return None + + tlist = node.get_executor().get_all_targets() + + task = self.tasker(self, tlist, node in self.original_top, node) + try: + task.make_ready() + except: + # We had a problem just trying to get this task ready (like + # a child couldn't be linked in to a VariantDir when deciding + # whether this node is current). Arrange to raise the + # exception when the Task is "executed." + self.ready_exc = sys.exc_info() + + if self.ready_exc: + task.exception_set(self.ready_exc) + + self.ready_exc = None + + return task + + def will_not_build(self, nodes, node_func=lambda n: None): + """ + Perform clean-up about nodes that will never be built. Invokes + a user defined function on all of these nodes (including all + of their parents). + """ + + T = self.trace + + pending_children = self.pending_children + + to_visit = set(nodes) + pending_children = pending_children - to_visit + + if T: + for n in nodes: + T.write(self.trace_message(' removing node %s from the pending children set\n' % + self.trace_node(n))) + try: + while len(to_visit): + node = to_visit.pop() + node_func(node) + + # Prune recursion by flushing the waiting children + # list immediately. + parents = node.waiting_parents + node.waiting_parents = set() + + to_visit = to_visit | parents + pending_children = pending_children - parents + + for p in parents: + p.ref_count = p.ref_count - 1 + if T: T.write(self.trace_message(' removing parent %s from the pending children set\n' % + self.trace_node(p))) + except KeyError: + # The container to_visit has been emptied. + pass + + # We have the stick back the pending_children list into the + # taskmaster because the python 1.5.2 compatibility does not + # allow us to use in-place updates + self.pending_children = pending_children + + def stop(self): + """ + Stops the current build completely. + """ + self.next_candidate = self.no_next_candidate + + def cleanup(self): + """ + Check for dependency cycles. + """ + if not self.pending_children: + return + + nclist = [(n, find_cycle([n], set())) for n in self.pending_children] + + genuine_cycles = [ + node for node,cycle in nclist + if cycle or node.get_state() != NODE_EXECUTED + ] + if not genuine_cycles: + # All of the "cycles" found were single nodes in EXECUTED state, + # which is to say, they really weren't cycles. Just return. + return + + desc = 'Found dependency cycle(s):\n' + for node, cycle in nclist: + if cycle: + desc = desc + " " + " -> ".join(map(str, cycle)) + "\n" + else: + desc = desc + \ + " Internal Error: no cycle found for node %s (%s) in state %s\n" % \ + (node, repr(node), StateString[node.get_state()]) + + raise SCons.Errors.UserError(desc) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/386asm.py b/scons/scons-local-2.3.0/SCons/Tool/386asm.py new file mode 100644 index 000000000..2b26c0f95 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/386asm.py @@ -0,0 +1,61 @@ +"""SCons.Tool.386asm + +Tool specification for the 386ASM assembler for the Phar Lap ETS embedded +operating system. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/386asm.py 2013/03/03 09:48:35 garyo" + +from SCons.Tool.PharLapCommon import addPharLapPaths +import SCons.Util + +as_module = __import__('as', globals(), locals(), []) + +def generate(env): + """Add Builders and construction variables for ar to an Environment.""" + as_module.generate(env) + + env['AS'] = '386asm' + env['ASFLAGS'] = SCons.Util.CLVar('') + env['ASPPFLAGS'] = '$ASFLAGS' + env['ASCOM'] = '$AS $ASFLAGS $SOURCES -o $TARGET' + env['ASPPCOM'] = '$CC $ASPPFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $SOURCES -o $TARGET' + + addPharLapPaths(env) + +def exists(env): + return env.Detect('386asm') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/BitKeeper.py b/scons/scons-local-2.3.0/SCons/Tool/BitKeeper.py new file mode 100644 index 000000000..288ef909f --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/BitKeeper.py @@ -0,0 +1,67 @@ +"""SCons.Tool.BitKeeper.py + +Tool-specific initialization for the BitKeeper source code control +system. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/BitKeeper.py 2013/03/03 09:48:35 garyo" + +import SCons.Action +import SCons.Builder +import SCons.Util + +def generate(env): + """Add a Builder factory function and construction variables for + BitKeeper to an Environment.""" + + def BitKeeperFactory(env=env): + """ """ + import SCons.Warnings as W + W.warn(W.DeprecatedSourceCodeWarning, """The BitKeeper() factory is deprecated and there is no replacement.""") + act = SCons.Action.Action("$BITKEEPERCOM", "$BITKEEPERCOMSTR") + return SCons.Builder.Builder(action = act, env = env) + + #setattr(env, 'BitKeeper', BitKeeperFactory) + env.BitKeeper = BitKeeperFactory + + env['BITKEEPER'] = 'bk' + env['BITKEEPERGET'] = '$BITKEEPER get' + env['BITKEEPERGETFLAGS'] = SCons.Util.CLVar('') + env['BITKEEPERCOM'] = '$BITKEEPERGET $BITKEEPERGETFLAGS $TARGET' + +def exists(env): + return env.Detect('bk') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/CVS.py b/scons/scons-local-2.3.0/SCons/Tool/CVS.py new file mode 100644 index 000000000..3e60643ee --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/CVS.py @@ -0,0 +1,73 @@ +"""SCons.Tool.CVS.py + +Tool-specific initialization for CVS. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Tool/CVS.py 2013/03/03 09:48:35 garyo" + +import SCons.Action +import SCons.Builder +import SCons.Util + +def generate(env): + """Add a Builder factory function and construction variables for + CVS to an Environment.""" + + def CVSFactory(repos, module='', env=env): + """ """ + import SCons.Warnings as W + W.warn(W.DeprecatedSourceCodeWarning, """The CVS() factory is deprecated and there is no replacement.""") + # fail if repos is not an absolute path name? + if module != '': + # Don't use os.path.join() because the name we fetch might + # be across a network and must use POSIX slashes as separators. + module = module + '/' + env['CVSCOM'] = '$CVS $CVSFLAGS co $CVSCOFLAGS -d ${TARGET.dir} $CVSMODULE${TARGET.posix}' + act = SCons.Action.Action('$CVSCOM', '$CVSCOMSTR') + return SCons.Builder.Builder(action = act, + env = env, + CVSREPOSITORY = repos, + CVSMODULE = module) + + #setattr(env, 'CVS', CVSFactory) + env.CVS = CVSFactory + + env['CVS'] = 'cvs' + env['CVSFLAGS'] = SCons.Util.CLVar('-d $CVSREPOSITORY') + env['CVSCOFLAGS'] = SCons.Util.CLVar('') + env['CVSCOM'] = '$CVS $CVSFLAGS co $CVSCOFLAGS ${TARGET.posix}' + +def exists(env): + return env.Detect('cvs') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/FortranCommon.py b/scons/scons-local-2.3.0/SCons/Tool/FortranCommon.py new file mode 100644 index 000000000..7b470a2a1 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/FortranCommon.py @@ -0,0 +1,263 @@ +"""SCons.Tool.FortranCommon + +Stuff for processing Fortran, common to all fortran dialects. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/FortranCommon.py 2013/03/03 09:48:35 garyo" + +import re +import os.path + +import SCons.Action +import SCons.Defaults +import SCons.Scanner.Fortran +import SCons.Tool +import SCons.Util + +def isfortran(env, source): + """Return 1 if any of code in source has fortran files in it, 0 + otherwise.""" + try: + fsuffixes = env['FORTRANSUFFIXES'] + except KeyError: + # If no FORTRANSUFFIXES, no fortran tool, so there is no need to look + # for fortran sources. + return 0 + + if not source: + # Source might be None for unusual cases like SConf. + return 0 + for s in source: + if s.sources: + ext = os.path.splitext(str(s.sources[0]))[1] + if ext in fsuffixes: + return 1 + return 0 + +def _fortranEmitter(target, source, env): + node = source[0].rfile() + if not node.exists() and not node.is_derived(): + print "Could not locate " + str(node.name) + return ([], []) + mod_regex = """(?i)^\s*MODULE\s+(?!PROCEDURE)(\w+)""" + cre = re.compile(mod_regex,re.M) + # Retrieve all USE'd module names + modules = cre.findall(node.get_text_contents()) + # Remove unique items from the list + modules = SCons.Util.unique(modules) + # Convert module name to a .mod filename + suffix = env.subst('$FORTRANMODSUFFIX', target=target, source=source) + moddir = env.subst('$FORTRANMODDIR', target=target, source=source) + modules = [x.lower() + suffix for x in modules] + for m in modules: + target.append(env.fs.File(m, moddir)) + return (target, source) + +def FortranEmitter(target, source, env): + target, source = _fortranEmitter(target, source, env) + return SCons.Defaults.StaticObjectEmitter(target, source, env) + +def ShFortranEmitter(target, source, env): + target, source = _fortranEmitter(target, source, env) + return SCons.Defaults.SharedObjectEmitter(target, source, env) + +def ComputeFortranSuffixes(suffixes, ppsuffixes): + """suffixes are fortran source files, and ppsuffixes the ones to be + pre-processed. Both should be sequences, not strings.""" + assert len(suffixes) > 0 + s = suffixes[0] + sup = s.upper() + upper_suffixes = [_.upper() for _ in suffixes] + if SCons.Util.case_sensitive_suffixes(s, sup): + ppsuffixes.extend(upper_suffixes) + else: + suffixes.extend(upper_suffixes) + +def CreateDialectActions(dialect): + """Create dialect specific actions.""" + CompAction = SCons.Action.Action('$%sCOM ' % dialect, '$%sCOMSTR' % dialect) + CompPPAction = SCons.Action.Action('$%sPPCOM ' % dialect, '$%sPPCOMSTR' % dialect) + ShCompAction = SCons.Action.Action('$SH%sCOM ' % dialect, '$SH%sCOMSTR' % dialect) + ShCompPPAction = SCons.Action.Action('$SH%sPPCOM ' % dialect, '$SH%sPPCOMSTR' % dialect) + + return CompAction, CompPPAction, ShCompAction, ShCompPPAction + +def DialectAddToEnv(env, dialect, suffixes, ppsuffixes, support_module = 0): + """Add dialect specific construction variables.""" + ComputeFortranSuffixes(suffixes, ppsuffixes) + + fscan = SCons.Scanner.Fortran.FortranScan("%sPATH" % dialect) + + for suffix in suffixes + ppsuffixes: + SCons.Tool.SourceFileScanner.add_scanner(suffix, fscan) + + env.AppendUnique(FORTRANSUFFIXES = suffixes + ppsuffixes) + + compaction, compppaction, shcompaction, shcompppaction = \ + CreateDialectActions(dialect) + + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + for suffix in suffixes: + static_obj.add_action(suffix, compaction) + shared_obj.add_action(suffix, shcompaction) + static_obj.add_emitter(suffix, FortranEmitter) + shared_obj.add_emitter(suffix, ShFortranEmitter) + + for suffix in ppsuffixes: + static_obj.add_action(suffix, compppaction) + shared_obj.add_action(suffix, shcompppaction) + static_obj.add_emitter(suffix, FortranEmitter) + shared_obj.add_emitter(suffix, ShFortranEmitter) + + if '%sFLAGS' % dialect not in env: + env['%sFLAGS' % dialect] = SCons.Util.CLVar('') + + if 'SH%sFLAGS' % dialect not in env: + env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS' % dialect) + + # If a tool does not define fortran prefix/suffix for include path, use C ones + if 'INC%sPREFIX' % dialect not in env: + env['INC%sPREFIX' % dialect] = '$INCPREFIX' + + if 'INC%sSUFFIX' % dialect not in env: + env['INC%sSUFFIX' % dialect] = '$INCSUFFIX' + + env['_%sINCFLAGS' % dialect] = '$( ${_concat(INC%sPREFIX, %sPATH, INC%sSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' % (dialect, dialect, dialect) + + if support_module == 1: + env['%sCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect) + env['%sPPCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect) + env['SH%sCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect) + env['SH%sPPCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect) + else: + env['%sCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect) + env['%sPPCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect) + env['SH%sCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect) + env['SH%sPPCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect) + +def add_fortran_to_env(env): + """Add Builders and construction variables for Fortran to an Environment.""" + try: + FortranSuffixes = env['FORTRANFILESUFFIXES'] + except KeyError: + FortranSuffixes = ['.f', '.for', '.ftn'] + + #print "Adding %s to fortran suffixes" % FortranSuffixes + try: + FortranPPSuffixes = env['FORTRANPPFILESUFFIXES'] + except KeyError: + FortranPPSuffixes = ['.fpp', '.FPP'] + + DialectAddToEnv(env, "FORTRAN", FortranSuffixes, + FortranPPSuffixes, support_module = 1) + + env['FORTRANMODPREFIX'] = '' # like $LIBPREFIX + env['FORTRANMODSUFFIX'] = '.mod' # like $LIBSUFFIX + + env['FORTRANMODDIR'] = '' # where the compiler should place .mod files + env['FORTRANMODDIRPREFIX'] = '' # some prefix to $FORTRANMODDIR - similar to $INCPREFIX + env['FORTRANMODDIRSUFFIX'] = '' # some suffix to $FORTRANMODDIR - similar to $INCSUFFIX + env['_FORTRANMODFLAG'] = '$( ${_concat(FORTRANMODDIRPREFIX, FORTRANMODDIR, FORTRANMODDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' + +def add_f77_to_env(env): + """Add Builders and construction variables for f77 to an Environment.""" + try: + F77Suffixes = env['F77FILESUFFIXES'] + except KeyError: + F77Suffixes = ['.f77'] + + #print "Adding %s to f77 suffixes" % F77Suffixes + try: + F77PPSuffixes = env['F77PPFILESUFFIXES'] + except KeyError: + F77PPSuffixes = [] + + DialectAddToEnv(env, "F77", F77Suffixes, F77PPSuffixes) + +def add_f90_to_env(env): + """Add Builders and construction variables for f90 to an Environment.""" + try: + F90Suffixes = env['F90FILESUFFIXES'] + except KeyError: + F90Suffixes = ['.f90'] + + #print "Adding %s to f90 suffixes" % F90Suffixes + try: + F90PPSuffixes = env['F90PPFILESUFFIXES'] + except KeyError: + F90PPSuffixes = [] + + DialectAddToEnv(env, "F90", F90Suffixes, F90PPSuffixes, + support_module = 1) + +def add_f95_to_env(env): + """Add Builders and construction variables for f95 to an Environment.""" + try: + F95Suffixes = env['F95FILESUFFIXES'] + except KeyError: + F95Suffixes = ['.f95'] + + #print "Adding %s to f95 suffixes" % F95Suffixes + try: + F95PPSuffixes = env['F95PPFILESUFFIXES'] + except KeyError: + F95PPSuffixes = [] + + DialectAddToEnv(env, "F95", F95Suffixes, F95PPSuffixes, + support_module = 1) + +def add_f03_to_env(env): + """Add Builders and construction variables for f03 to an Environment.""" + try: + F03Suffixes = env['F03FILESUFFIXES'] + except KeyError: + F03Suffixes = ['.f03'] + + #print "Adding %s to f95 suffixes" % F95Suffixes + try: + F03PPSuffixes = env['F03PPFILESUFFIXES'] + except KeyError: + F03PPSuffixes = [] + + DialectAddToEnv(env, "F03", F03Suffixes, F03PPSuffixes, + support_module = 1) + +def add_all_to_env(env): + """Add builders and construction variables for all supported fortran + dialects.""" + add_fortran_to_env(env) + add_f77_to_env(env) + add_f90_to_env(env) + add_f95_to_env(env) + add_f03_to_env(env) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/GettextCommon.py b/scons/scons-local-2.3.0/SCons/Tool/GettextCommon.py new file mode 100644 index 000000000..2b1b9254c --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/GettextCommon.py @@ -0,0 +1,430 @@ +"""SCons.Tool.GettextCommon module + +Used by several tools of `gettext` toolset. +""" + +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Tool/GettextCommon.py 2013/03/03 09:48:35 garyo" + +import SCons.Warnings +import re + +############################################################################# +class XgettextToolWarning(SCons.Warnings.Warning): pass +class XgettextNotFound(XgettextToolWarning): pass +class MsginitToolWarning(SCons.Warnings.Warning): pass +class MsginitNotFound(MsginitToolWarning): pass +class MsgmergeToolWarning(SCons.Warnings.Warning): pass +class MsgmergeNotFound(MsgmergeToolWarning): pass +class MsgfmtToolWarning(SCons.Warnings.Warning): pass +class MsgfmtNotFound(MsgfmtToolWarning): pass +############################################################################# +SCons.Warnings.enableWarningClass(XgettextToolWarning) +SCons.Warnings.enableWarningClass(XgettextNotFound) +SCons.Warnings.enableWarningClass(MsginitToolWarning) +SCons.Warnings.enableWarningClass(MsginitNotFound) +SCons.Warnings.enableWarningClass(MsgmergeToolWarning) +SCons.Warnings.enableWarningClass(MsgmergeNotFound) +SCons.Warnings.enableWarningClass(MsgfmtToolWarning) +SCons.Warnings.enableWarningClass(MsgfmtNotFound) +############################################################################# + +############################################################################# +class _POTargetFactory(object): + """ A factory of `PO` target files. + + Factory defaults differ from these of `SCons.Node.FS.FS`. We set `precious` + (this is required by builders and actions gettext) and `noclean` flags by + default for all produced nodes. + """ + def __init__( self, env, nodefault = True, alias = None, precious = True + , noclean = True ): + """ Object constructor. + + **Arguments** + + - *env* (`SCons.Environment.Environment`) + - *nodefault* (`boolean`) - if `True`, produced nodes will be ignored + from default target `'.'` + - *alias* (`string`) - if provided, produced nodes will be automatically + added to this alias, and alias will be set as `AlwaysBuild` + - *precious* (`boolean`) - if `True`, the produced nodes will be set as + `Precious`. + - *noclen* (`boolean`) - if `True`, the produced nodes will be excluded + from `Clean`. + """ + self.env = env + self.alias = alias + self.precious = precious + self.noclean = noclean + self.nodefault = nodefault + + def _create_node(self, name, factory, directory = None, create = 1): + """ Create node, and set it up to factory settings. """ + import SCons.Util + node = factory(name, directory, create) + node.set_noclean(self.noclean) + node.set_precious(self.precious) + if self.nodefault: + self.env.Ignore('.', node) + if self.alias: + self.env.AlwaysBuild(self.env.Alias(self.alias, node)) + return node + + def Entry(self, name, directory = None, create = 1): + """ Create `SCons.Node.FS.Entry` """ + return self._create_node(name, self.env.fs.Entry, directory, create) + + def File(self, name, directory = None, create = 1): + """ Create `SCons.Node.FS.File` """ + return self._create_node(name, self.env.fs.File, directory, create) +############################################################################# + +############################################################################# +_re_comment = re.compile(r'(#[^\n\r]+)$', re.M) +_re_lang = re.compile(r'([a-zA-Z0-9_]+)', re.M) +############################################################################# +def _read_linguas_from_files(env, linguas_files = None): + """ Parse `LINGUAS` file and return list of extracted languages """ + import SCons.Util + import SCons.Environment + global _re_comment + global _re_lang + if not SCons.Util.is_List(linguas_files) \ + and not SCons.Util.is_String(linguas_files) \ + and not isinstance(linguas_files, SCons.Node.FS.Base) \ + and linguas_files: + # If, linguas_files==True or such, then read 'LINGUAS' file. + linguas_files = [ 'LINGUAS' ] + if linguas_files is None: + return [] + fnodes = env.arg2nodes(linguas_files) + linguas = [] + for fnode in fnodes: + contents = _re_comment.sub("", fnode.get_text_contents()) + ls = [ l for l in _re_lang.findall(contents) if l ] + linguas.extend(ls) + return linguas +############################################################################# + +############################################################################# +from SCons.Builder import BuilderBase +############################################################################# +class _POFileBuilder(BuilderBase): + """ `PO` file builder. + + This is multi-target single-source builder. In typical situation the source + is single `POT` file, e.g. `messages.pot`, and there are multiple `PO` + targets to be updated from this `POT`. We must run + `SCons.Builder.BuilderBase._execute()` separatelly for each target to track + dependencies separatelly for each target file. + + **NOTE**: if we call `SCons.Builder.BuilderBase._execute(.., target, ...)` + with target being list of all targets, all targets would be rebuilt each time + one of the targets from this list is missing. This would happen, for example, + when new language `ll` enters `LINGUAS_FILE` (at this moment there is no + `ll.po` file yet). To avoid this, we override + `SCons.Builder.BuilerBase._execute()` and call it separatelly for each + target. Here we also append to the target list the languages read from + `LINGUAS_FILE`. + """ + # + #* The argument for overriding _execute(): We must use environment with + # builder overrides applied (see BuilderBase.__init__(). Here it comes for + # free. + #* The argument against using 'emitter': The emitter is called too late + # by BuilderBase._execute(). If user calls, for example: + # + # env.POUpdate(LINGUAS_FILE = 'LINGUAS') + # + # the builder throws error, because it is called with target=None, + # source=None and is trying to "generate" sources or target list first. + # If user calls + # + # env.POUpdate(['foo', 'baz'], LINGUAS_FILE = 'LINGUAS') + # + # the env.BuilderWrapper() calls our builder with target=None, + # source=['foo', 'baz']. The BuilderBase._execute() then splits execution + # and execute iterativelly (recursion) self._execute(None, source[i]). + # After that it calls emitter (which is quite too late). The emitter is + # also called in each iteration, what makes things yet worse. + def __init__(self, env, **kw): + if not 'suffix' in kw: + kw['suffix'] = '$POSUFFIX' + if not 'src_suffix' in kw: + kw['src_suffix'] = '$POTSUFFIX' + if not 'src_builder' in kw: + kw['src_builder'] = '_POTUpdateBuilder' + if not 'single_source' in kw: + kw['single_source'] = True + alias = None + if 'target_alias' in kw: + alias = kw['target_alias'] + del kw['target_alias'] + if not 'target_factory' in kw: + kw['target_factory'] = _POTargetFactory(env, alias=alias).File + BuilderBase.__init__(self, **kw) + + def _execute(self, env, target, source, *args, **kw): + """ Execute builder's actions. + + Here we append to `target` the languages read from `$LINGUAS_FILE` and + apply `SCons.Builder.BuilderBase._execute()` separatelly to each target. + The arguments and return value are same as for + `SCons.Builder.BuilderBase._execute()`. + """ + import SCons.Util + import SCons.Node + linguas_files = None + if env.has_key('LINGUAS_FILE') and env['LINGUAS_FILE']: + linguas_files = env['LINGUAS_FILE'] + # This prevents endless recursion loop (we'll be invoked once for + # each target appended here, we must not extend the list again). + env['LINGUAS_FILE'] = None + linguas = _read_linguas_from_files(env,linguas_files) + if SCons.Util.is_List(target): + target.extend(linguas) + elif target is not None: + target = [target] + linguas + else: + target = linguas + if not target: + # Let the SCons.BuilderBase to handle this patologic situation + return BuilderBase._execute( self, env, target, source, *args, **kw) + # The rest is ours + if not SCons.Util.is_List(target): + target = [ target ] + result = [] + for tgt in target: + r = BuilderBase._execute( self, env, [tgt], source, *args, **kw) + result.extend(r) + if linguas_files is not None: + env['LINGUAS_FILE'] = linguas_files + return SCons.Node.NodeList(result) +############################################################################# + +import SCons.Environment +############################################################################# +def _translate(env, target=None, source=SCons.Environment._null, *args, **kw): + """ Function for `Translate()` pseudo-builder """ + if target is None: target = [] + pot = env.POTUpdate(None, source, *args, **kw) + po = env.POUpdate(target, pot, *args, **kw) + return po +############################################################################# + +############################################################################# +class RPaths(object): + """ Callable object, which returns pathnames relative to SCons current + working directory. + + It seems like `SCons.Node.FS.Base.get_path()` returns absolute paths + for nodes that are outside of current working directory (`env.fs.getcwd()`). + Here, we often have `SConscript`, `POT` and `PO` files within `po/` + directory and source files (e.g. `*.c`) outside of it. When generating `POT` + template file, references to source files are written to `POT` template, so + a translator may later quickly jump to appropriate source file and line from + its `PO` editor (e.g. `poedit`). Relative paths in `PO` file are usually + interpreted by `PO` editor as paths relative to the place, where `PO` file + lives. The absolute paths would make resultant `POT` file nonportable, as + the references would be correct only on the machine, where `POT` file was + recently re-created. For such reason, we need a function, which always + returns relative paths. This is the purpose of `RPaths` callable object. + + The `__call__` method returns paths relative to current woking directory, but + we assume, that *xgettext(1)* is run from the directory, where target file is + going to be created. + + Note, that this may not work for files distributed over several hosts or + across different drives on windows. We assume here, that single local + filesystem holds both source files and target `POT` templates. + + Intended use of `RPaths` - in `xgettext.py`:: + + def generate(env): + from GettextCommon import RPaths + ... + sources = '$( ${_concat( "", SOURCES, "", __env__, XgettextRPaths, TARGET, SOURCES)} $)' + env.Append( + ... + XGETTEXTCOM = 'XGETTEXT ... ' + sources, + ... + XgettextRPaths = RPaths(env) + ) + """ + # NOTE: This callable object returns pathnames of dirs/files relative to + # current working directory. The pathname remains relative also for entries + # that are outside of current working directory (node, that + # SCons.Node.FS.File and siblings return absolute path in such case). For + # simplicity we compute path relative to current working directory, this + # seems be enough for our purposes (don't need TARGET variable and + # SCons.Defaults.Variable_Caller stuff). + + def __init__(self, env): + """ Initialize `RPaths` callable object. + + **Arguments**: + + - *env* - a `SCons.Environment.Environment` object, defines *current + working dir*. + """ + self.env = env + + # FIXME: I'm not sure, how it should be implemented (what the *args are in + # general, what is **kw). + def __call__(self, nodes, *args, **kw): + """ Return nodes' paths (strings) relative to current working directory. + + **Arguments**: + + - *nodes* ([`SCons.Node.FS.Base`]) - list of nodes. + - *args* - currently unused. + - *kw* - currently unused. + + **Returns**: + + - Tuple of strings, which represent paths relative to current working + directory (for given environment). + """ + # os.path.relpath is available only on python >= 2.6. We use our own + # implementation. It's taken from BareNecessities package: + # http://jimmyg.org/work/code/barenecessities/index.html + from posixpath import curdir + def relpath(path, start=curdir): + import posixpath + """Return a relative version of a path""" + if not path: + raise ValueError("no path specified") + start_list = posixpath.abspath(start).split(posixpath.sep) + path_list = posixpath.abspath(path).split(posixpath.sep) + # Work out how much of the filepath is shared by start and path. + i = len(posixpath.commonprefix([start_list, path_list])) + rel_list = [posixpath.pardir] * (len(start_list)-i) + path_list[i:] + if not rel_list: + return posixpath.curdir + return posixpath.join(*rel_list) + import os + import SCons.Node.FS + rpaths = () + cwd = self.env.fs.getcwd().get_abspath() + for node in nodes: + rpath = None + if isinstance(node, SCons.Node.FS.Base): + rpath = relpath(node.get_abspath(), cwd) + # FIXME: Other types possible here? + if rpath is not None: + rpaths += (rpath,) + return rpaths +############################################################################# + +############################################################################# +def _init_po_files(target, source, env): + """ Action function for `POInit` builder. """ + nop = lambda target, source, env : 0 + if env.has_key('POAUTOINIT'): + autoinit = env['POAUTOINIT'] + else: + autoinit = False + # Well, if everything outside works well, this loop should do single + # iteration. Otherwise we are rebuilding all the targets even, if just + # one has changed (but is this out fault?). + for tgt in target: + if not tgt.exists(): + if autoinit: + action = SCons.Action.Action('$MSGINITCOM', '$MSGINITCOMSTR') + else: + msg = 'File ' + repr(str(tgt)) + ' does not exist. ' \ + + 'If you are a translator, you can create it through: \n' \ + + '$MSGINITCOM' + action = SCons.Action.Action(nop, msg) + status = action([tgt], source, env) + if status: return status + return 0 +############################################################################# + +############################################################################# +def _detect_xgettext(env): + """ Detects *xgettext(1)* binary """ + if env.has_key('XGETTEXT'): + return env['XGETTEXT'] + xgettext = env.Detect('xgettext'); + if xgettext: + return xgettext + raise SCons.Errors.StopError(XgettextNotFound,"Could not detect xgettext") + return None +############################################################################# +def _xgettext_exists(env): + return _detect_xgettext(env) +############################################################################# + +############################################################################# +def _detect_msginit(env): + """ Detects *msginit(1)* program. """ + if env.has_key('MSGINIT'): + return env['MSGINIT'] + msginit = env.Detect('msginit'); + if msginit: + return msginit + raise SCons.Errors.StopError(MsginitNotFound, "Could not detect msginit") + return None +############################################################################# +def _msginit_exists(env): + return _detect_msginit(env) +############################################################################# + +############################################################################# +def _detect_msgmerge(env): + """ Detects *msgmerge(1)* program. """ + if env.has_key('MSGMERGE'): + return env['MSGMERGE'] + msgmerge = env.Detect('msgmerge'); + if msgmerge: + return msgmerge + raise SCons.Errors.StopError(MsgmergeNotFound, "Could not detect msgmerge") + return None +############################################################################# +def _msgmerge_exists(env): + return _detect_msgmerge(env) +############################################################################# + +############################################################################# +def _detect_msgfmt(env): + """ Detects *msgmfmt(1)* program. """ + if env.has_key('MSGFMT'): + return env['MSGFMT'] + msgfmt = env.Detect('msgfmt'); + if msgfmt: + return msgfmt + raise SCons.Errors.StopError(MsgfmtNotFound, "Could not detect msgfmt") + return None +############################################################################# +def _msgfmt_exists(env): + return _detect_msgfmt(env) +############################################################################# + +############################################################################# +def tool_list(platform, env): + """ List tools that shall be generated by top-level `gettext` tool """ + return [ 'xgettext', 'msginit', 'msgmerge', 'msgfmt' ] +############################################################################# + diff --git a/scons/scons-local-2.3.0/SCons/Tool/JavaCommon.py b/scons/scons-local-2.3.0/SCons/Tool/JavaCommon.py new file mode 100644 index 000000000..ea5e5bd58 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/JavaCommon.py @@ -0,0 +1,323 @@ +"""SCons.Tool.JavaCommon + +Stuff for processing Java. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/JavaCommon.py 2013/03/03 09:48:35 garyo" + +import os +import os.path +import re + +java_parsing = 1 + +default_java_version = '1.4' + +if java_parsing: + # Parse Java files for class names. + # + # This is a really cool parser from Charles Crain + # that finds appropriate class names in Java source. + + # A regular expression that will find, in a java file: + # newlines; + # double-backslashes; + # a single-line comment "//"; + # single or double quotes preceeded by a backslash; + # single quotes, double quotes, open or close braces, semi-colons, + # periods, open or close parentheses; + # floating-point numbers; + # any alphanumeric token (keyword, class name, specifier); + # any alphanumeric token surrounded by angle brackets (generics); + # the multi-line comment begin and end tokens /* and */; + # array declarations "[]". + _reToken = re.compile(r'(\n|\\\\|//|\\[\'"]|[\'"\{\}\;\.\(\)]|' + + r'\d*\.\d*|[A-Za-z_][\w\$\.]*|<[A-Za-z_]\w+>|' + + r'/\*|\*/|\[\])') + + class OuterState(object): + """The initial state for parsing a Java file for classes, + interfaces, and anonymous inner classes.""" + def __init__(self, version=default_java_version): + + if not version in ('1.1', '1.2', '1.3','1.4', '1.5', '1.6', '1.7', + '5', '6'): + msg = "Java version %s not supported" % version + raise NotImplementedError(msg) + + self.version = version + self.listClasses = [] + self.listOutputs = [] + self.stackBrackets = [] + self.brackets = 0 + self.nextAnon = 1 + self.localClasses = [] + self.stackAnonClassBrackets = [] + self.anonStacksStack = [[0]] + self.package = None + + def trace(self): + pass + + def __getClassState(self): + try: + return self.classState + except AttributeError: + ret = ClassState(self) + self.classState = ret + return ret + + def __getPackageState(self): + try: + return self.packageState + except AttributeError: + ret = PackageState(self) + self.packageState = ret + return ret + + def __getAnonClassState(self): + try: + return self.anonState + except AttributeError: + self.outer_state = self + ret = SkipState(1, AnonClassState(self)) + self.anonState = ret + return ret + + def __getSkipState(self): + try: + return self.skipState + except AttributeError: + ret = SkipState(1, self) + self.skipState = ret + return ret + + def __getAnonStack(self): + return self.anonStacksStack[-1] + + def openBracket(self): + self.brackets = self.brackets + 1 + + def closeBracket(self): + self.brackets = self.brackets - 1 + if len(self.stackBrackets) and \ + self.brackets == self.stackBrackets[-1]: + self.listOutputs.append('$'.join(self.listClasses)) + self.localClasses.pop() + self.listClasses.pop() + self.anonStacksStack.pop() + self.stackBrackets.pop() + if len(self.stackAnonClassBrackets) and \ + self.brackets == self.stackAnonClassBrackets[-1]: + self.__getAnonStack().pop() + self.stackAnonClassBrackets.pop() + + def parseToken(self, token): + if token[:2] == '//': + return IgnoreState('\n', self) + elif token == '/*': + return IgnoreState('*/', self) + elif token == '{': + self.openBracket() + elif token == '}': + self.closeBracket() + elif token in [ '"', "'" ]: + return IgnoreState(token, self) + elif token == "new": + # anonymous inner class + if len(self.listClasses) > 0: + return self.__getAnonClassState() + return self.__getSkipState() # Skip the class name + elif token in ['class', 'interface', 'enum']: + if len(self.listClasses) == 0: + self.nextAnon = 1 + self.stackBrackets.append(self.brackets) + return self.__getClassState() + elif token == 'package': + return self.__getPackageState() + elif token == '.': + # Skip the attribute, it might be named "class", in which + # case we don't want to treat the following token as + # an inner class name... + return self.__getSkipState() + return self + + def addAnonClass(self): + """Add an anonymous inner class""" + if self.version in ('1.1', '1.2', '1.3', '1.4'): + clazz = self.listClasses[0] + self.listOutputs.append('%s$%d' % (clazz, self.nextAnon)) + elif self.version in ('1.5', '1.6', '1.7', '5', '6'): + self.stackAnonClassBrackets.append(self.brackets) + className = [] + className.extend(self.listClasses) + self.__getAnonStack()[-1] = self.__getAnonStack()[-1] + 1 + for anon in self.__getAnonStack(): + className.append(str(anon)) + self.listOutputs.append('$'.join(className)) + + self.nextAnon = self.nextAnon + 1 + self.__getAnonStack().append(0) + + def setPackage(self, package): + self.package = package + + class AnonClassState(object): + """A state that looks for anonymous inner classes.""" + def __init__(self, old_state): + # outer_state is always an instance of OuterState + self.outer_state = old_state.outer_state + self.old_state = old_state + self.brace_level = 0 + def parseToken(self, token): + # This is an anonymous class if and only if the next + # non-whitespace token is a bracket. Everything between + # braces should be parsed as normal java code. + if token[:2] == '//': + return IgnoreState('\n', self) + elif token == '/*': + return IgnoreState('*/', self) + elif token == '\n': + return self + elif token[0] == '<' and token[-1] == '>': + return self + elif token == '(': + self.brace_level = self.brace_level + 1 + return self + if self.brace_level > 0: + if token == 'new': + # look further for anonymous inner class + return SkipState(1, AnonClassState(self)) + elif token in [ '"', "'" ]: + return IgnoreState(token, self) + elif token == ')': + self.brace_level = self.brace_level - 1 + return self + if token == '{': + self.outer_state.addAnonClass() + return self.old_state.parseToken(token) + + class SkipState(object): + """A state that will skip a specified number of tokens before + reverting to the previous state.""" + def __init__(self, tokens_to_skip, old_state): + self.tokens_to_skip = tokens_to_skip + self.old_state = old_state + def parseToken(self, token): + self.tokens_to_skip = self.tokens_to_skip - 1 + if self.tokens_to_skip < 1: + return self.old_state + return self + + class ClassState(object): + """A state we go into when we hit a class or interface keyword.""" + def __init__(self, outer_state): + # outer_state is always an instance of OuterState + self.outer_state = outer_state + def parseToken(self, token): + # the next non-whitespace token should be the name of the class + if token == '\n': + return self + # If that's an inner class which is declared in a method, it + # requires an index prepended to the class-name, e.g. + # 'Foo$1Inner' (Tigris Issue 2087) + if self.outer_state.localClasses and \ + self.outer_state.stackBrackets[-1] > \ + self.outer_state.stackBrackets[-2]+1: + locals = self.outer_state.localClasses[-1] + try: + idx = locals[token] + locals[token] = locals[token]+1 + except KeyError: + locals[token] = 1 + token = str(locals[token]) + token + self.outer_state.localClasses.append({}) + self.outer_state.listClasses.append(token) + self.outer_state.anonStacksStack.append([0]) + return self.outer_state + + class IgnoreState(object): + """A state that will ignore all tokens until it gets to a + specified token.""" + def __init__(self, ignore_until, old_state): + self.ignore_until = ignore_until + self.old_state = old_state + def parseToken(self, token): + if self.ignore_until == token: + return self.old_state + return self + + class PackageState(object): + """The state we enter when we encounter the package keyword. + We assume the next token will be the package name.""" + def __init__(self, outer_state): + # outer_state is always an instance of OuterState + self.outer_state = outer_state + def parseToken(self, token): + self.outer_state.setPackage(token) + return self.outer_state + + def parse_java_file(fn, version=default_java_version): + return parse_java(open(fn, 'r').read(), version) + + def parse_java(contents, version=default_java_version, trace=None): + """Parse a .java file and return a double of package directory, + plus a list of .class files that compiling that .java file will + produce""" + package = None + initial = OuterState(version) + currstate = initial + for token in _reToken.findall(contents): + # The regex produces a bunch of groups, but only one will + # have anything in it. + currstate = currstate.parseToken(token) + if trace: trace(token, currstate) + if initial.package: + package = initial.package.replace('.', os.sep) + return (package, initial.listOutputs) + +else: + # Don't actually parse Java files for class names. + # + # We might make this a configurable option in the future if + # Java-file parsing takes too long (although it shouldn't relative + # to how long the Java compiler itself seems to take...). + + def parse_java_file(fn): + """ "Parse" a .java file. + + This actually just splits the file name, so the assumption here + is that the file name matches the public class name, and that + the path to the file is the same as the package name. + """ + return os.path.split(file) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/MSCommon/__init__.py b/scons/scons-local-2.3.0/SCons/Tool/MSCommon/__init__.py new file mode 100644 index 000000000..37226633b --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/MSCommon/__init__.py @@ -0,0 +1,56 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/MSCommon/__init__.py 2013/03/03 09:48:35 garyo" + +__doc__ = """ +Common functions for Microsoft Visual Studio and Visual C/C++. +""" + +import copy +import os +import re +import subprocess + +import SCons.Errors +import SCons.Platform.win32 +import SCons.Util + +from SCons.Tool.MSCommon.sdk import mssdk_exists, \ + mssdk_setup_env + +from SCons.Tool.MSCommon.vc import msvc_exists, \ + msvc_setup_env, \ + msvc_setup_env_once + +from SCons.Tool.MSCommon.vs import get_default_version, \ + get_vs_by_version, \ + merge_default_version, \ + msvs_exists, \ + query_versions + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/MSCommon/arch.py b/scons/scons-local-2.3.0/SCons/Tool/MSCommon/arch.py new file mode 100644 index 000000000..e6eb38228 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/MSCommon/arch.py @@ -0,0 +1,61 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/MSCommon/arch.py 2013/03/03 09:48:35 garyo" + +__doc__ = """Module to define supported Windows chip architectures. +""" + +import os + +class ArchDefinition(object): + """ + A class for defining architecture-specific settings and logic. + """ + def __init__(self, arch, synonyms=[]): + self.arch = arch + self.synonyms = synonyms + +SupportedArchitectureList = [ + ArchitectureDefinition( + 'x86', + ['i386', 'i486', 'i586', 'i686'], + ), + + ArchitectureDefinition( + 'x86_64', + ['AMD64', 'amd64', 'em64t', 'EM64T', 'x86_64'], + ), + + ArchitectureDefinition( + 'ia64', + ['IA64'], + ), +] + +SupportedArchitectureMap = {} +for a in SupportedArchitectureList: + SupportedArchitectureMap[a.arch] = a + for s in a.synonyms: + SupportedArchitectureMap[s] = a + diff --git a/scons/scons-local-2.3.0/SCons/Tool/MSCommon/common.py b/scons/scons-local-2.3.0/SCons/Tool/MSCommon/common.py new file mode 100644 index 000000000..e3fda5af1 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/MSCommon/common.py @@ -0,0 +1,242 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/MSCommon/common.py 2013/03/03 09:48:35 garyo" + +__doc__ = """ +Common helper functions for working with the Microsoft tool chain. +""" + +import copy +import os +import subprocess +import re + +import SCons.Util + + +logfile = os.environ.get('SCONS_MSCOMMON_DEBUG') +if logfile == '-': + def debug(x): + print x +elif logfile: + try: + import logging + except ImportError: + debug = lambda x: open(logfile, 'a').write(x + '\n') + else: + logging.basicConfig(filename=logfile, level=logging.DEBUG) + debug = logging.debug +else: + debug = lambda x: None + + +_is_win64 = None + +def is_win64(): + """Return true if running on windows 64 bits. + + Works whether python itself runs in 64 bits or 32 bits.""" + # Unfortunately, python does not provide a useful way to determine + # if the underlying Windows OS is 32-bit or 64-bit. Worse, whether + # the Python itself is 32-bit or 64-bit affects what it returns, + # so nothing in sys.* or os.* help. + + # Apparently the best solution is to use env vars that Windows + # sets. If PROCESSOR_ARCHITECTURE is not x86, then the python + # process is running in 64 bit mode (on a 64-bit OS, 64-bit + # hardware, obviously). + # If this python is 32-bit but the OS is 64, Windows will set + # ProgramW6432 and PROCESSOR_ARCHITEW6432 to non-null. + # (Checking for HKLM\Software\Wow6432Node in the registry doesn't + # work, because some 32-bit installers create it.) + global _is_win64 + if _is_win64 is None: + # I structured these tests to make it easy to add new ones or + # add exceptions in the future, because this is a bit fragile. + _is_win64 = False + if os.environ.get('PROCESSOR_ARCHITECTURE','x86') != 'x86': + _is_win64 = True + if os.environ.get('PROCESSOR_ARCHITEW6432'): + _is_win64 = True + if os.environ.get('ProgramW6432'): + _is_win64 = True + return _is_win64 + + +def read_reg(value): + return SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, value)[0] + +def has_reg(value): + """Return True if the given key exists in HKEY_LOCAL_MACHINE, False + otherwise.""" + try: + SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, value) + ret = True + except WindowsError: + ret = False + return ret + +# Functions for fetching environment variable settings from batch files. + +def normalize_env(env, keys, force=False): + """Given a dictionary representing a shell environment, add the variables + from os.environ needed for the processing of .bat files; the keys are + controlled by the keys argument. + + It also makes sure the environment values are correctly encoded. + + If force=True, then all of the key values that exist are copied + into the returned dictionary. If force=false, values are only + copied if the key does not already exist in the copied dictionary. + + Note: the environment is copied.""" + normenv = {} + if env: + for k in env.keys(): + normenv[k] = copy.deepcopy(env[k]).encode('mbcs') + + for k in keys: + if k in os.environ and (force or not k in normenv): + normenv[k] = os.environ[k].encode('mbcs') + + return normenv + +def get_output(vcbat, args = None, env = None): + """Parse the output of given bat file, with given args.""" + + if env is None: + # Create a blank environment, for use in launching the tools + env = SCons.Environment.Environment(tools=[]) + + # TODO: This is a hard-coded list of the variables that (may) need + # to be imported from os.environ[] for v[sc]*vars*.bat file + # execution to work. This list should really be either directly + # controlled by vc.py, or else derived from the common_tools_var + # settings in vs.py. + vars = [ + 'COMSPEC', + 'VS110COMNTOOLS', + 'VS100COMNTOOLS', + 'VS90COMNTOOLS', + 'VS80COMNTOOLS', + 'VS71COMNTOOLS', + 'VS70COMNTOOLS', + 'VS60COMNTOOLS', + ] + env['ENV'] = normalize_env(env['ENV'], vars, force=False) + + if args: + debug("Calling '%s %s'" % (vcbat, args)) + popen = SCons.Action._subproc(env, + '"%s" %s & set' % (vcbat, args), + stdin = 'devnull', + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + else: + debug("Calling '%s'" % vcbat) + popen = SCons.Action._subproc(env, + '"%s" & set' % vcbat, + stdin = 'devnull', + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + # Use the .stdout and .stderr attributes directly because the + # .communicate() method uses the threading module on Windows + # and won't work under Pythons not built with threading. + stdout = popen.stdout.read() + stderr = popen.stderr.read() + if stderr: + # TODO: find something better to do with stderr; + # this at least prevents errors from getting swallowed. + import sys + sys.stderr.write(stderr) + if popen.wait() != 0: + raise IOError(stderr.decode("mbcs")) + + output = stdout.decode("mbcs") + return output + +def parse_output(output, keep = ("INCLUDE", "LIB", "LIBPATH", "PATH")): + # dkeep is a dict associating key: path_list, where key is one item from + # keep, and pat_list the associated list of paths + + dkeep = dict([(i, []) for i in keep]) + + # rdk will keep the regex to match the .bat file output line starts + rdk = {} + for i in keep: + rdk[i] = re.compile('%s=(.*)' % i, re.I) + + def add_env(rmatch, key, dkeep=dkeep): + plist = rmatch.group(1).split(os.pathsep) + for p in plist: + # Do not add empty paths (when a var ends with ;) + if p: + p = p.encode('mbcs') + # XXX: For some reason, VC98 .bat file adds "" around the PATH + # values, and it screws up the environment later, so we strip + # it. + p = p.strip('"') + dkeep[key].append(p) + + for line in output.splitlines(): + for k,v in rdk.items(): + m = v.match(line) + if m: + add_env(m, k) + + return dkeep + +# TODO(sgk): unused +def output_to_dict(output): + """Given an output string, parse it to find env variables. + + Return a dict where keys are variables names, and values their content""" + envlinem = re.compile(r'^([a-zA-z0-9]+)=([\S\s]*)$') + parsedenv = {} + for line in output.splitlines(): + m = envlinem.match(line) + if m: + parsedenv[m.group(1)] = m.group(2) + return parsedenv + +# TODO(sgk): unused +def get_new(l1, l2): + """Given two list l1 and l2, return the items in l2 which are not in l1. + Order is maintained.""" + + # We don't try to be smart: lists are small, and this is not the bottleneck + # is any case + new = [] + for i in l2: + if i not in l1: + new.append(i) + + return new + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/MSCommon/netframework.py b/scons/scons-local-2.3.0/SCons/Tool/MSCommon/netframework.py new file mode 100644 index 000000000..e1c12a643 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/MSCommon/netframework.py @@ -0,0 +1,82 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Tool/MSCommon/netframework.py 2013/03/03 09:48:35 garyo" + +__doc__ = """ +""" + +import os +import re + +from common import read_reg, debug + +# Original value recorded by dcournapeau +_FRAMEWORKDIR_HKEY_ROOT = r'Software\Microsoft\.NETFramework\InstallRoot' +# On SGK's system +_FRAMEWORKDIR_HKEY_ROOT = r'Software\Microsoft\Microsoft SDKs\.NETFramework\v2.0\InstallationFolder' + +def find_framework_root(): + # XXX: find it from environment (FrameworkDir) + try: + froot = read_reg(_FRAMEWORKDIR_HKEY_ROOT) + debug("Found framework install root in registry: %s" % froot) + except WindowsError, e: + debug("Could not read reg key %s" % _FRAMEWORKDIR_HKEY_ROOT) + return None + + if not os.path.exists(froot): + debug("%s not found on fs" % froot) + return None + + return froot + +def query_versions(): + froot = find_framework_root() + if froot: + contents = os.listdir(froot) + + l = re.compile('v[0-9]+.*') + versions = [e for e in contents if l.match(e)] + + def versrt(a,b): + # since version numbers aren't really floats... + aa = a[1:] + bb = b[1:] + aal = aa.split('.') + bbl = bb.split('.') + # sequence comparison in python is lexicographical + # which is exactly what we want. + # Note we sort backwards so the highest version is first. + return cmp(bbl,aal) + + versions.sort(versrt) + else: + versions = [] + + return versions + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/MSCommon/sdk.py b/scons/scons-local-2.3.0/SCons/Tool/MSCommon/sdk.py new file mode 100644 index 000000000..1536a22bf --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/MSCommon/sdk.py @@ -0,0 +1,391 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/MSCommon/sdk.py 2013/03/03 09:48:35 garyo" + +__doc__ = """Module to detect the Platform/Windows SDK + +PSDK 2003 R1 is the earliest version detected. +""" + +import os + +import SCons.Errors +import SCons.Util + +import common + +debug = common.debug + +# SDK Checks. This is of course a mess as everything else on MS platforms. Here +# is what we do to detect the SDK: +# +# For Windows SDK >= 6.0: just look into the registry entries: +# HKLM\Software\Microsoft\Microsoft SDKs\Windows +# All the keys in there are the available versions. +# +# For Platform SDK before 6.0 (2003 server R1 and R2, etc...), there does not +# seem to be any sane registry key, so the precise location is hardcoded. +# +# For versions below 2003R1, it seems the PSDK is included with Visual Studio? +# +# Also, per the following: +# http://benjamin.smedbergs.us/blog/tag/atl/ +# VC++ Professional comes with the SDK, VC++ Express does not. + +# Location of the SDK (checked for 6.1 only) +_CURINSTALLED_SDK_HKEY_ROOT = \ + r"Software\Microsoft\Microsoft SDKs\Windows\CurrentInstallFolder" + + +class SDKDefinition(object): + """ + An abstract base class for trying to find installed SDK directories. + """ + def __init__(self, version, **kw): + self.version = version + self.__dict__.update(kw) + + def find_sdk_dir(self): + """Try to find the MS SDK from the registry. + + Return None if failed or the directory does not exist. + """ + if not SCons.Util.can_read_reg: + debug('find_sdk_dir(): can not read registry') + return None + + hkey = self.HKEY_FMT % self.hkey_data + debug('find_sdk_dir(): checking registry:%s'%hkey) + + try: + sdk_dir = common.read_reg(hkey) + except WindowsError, e: + debug('find_sdk_dir(): no SDK registry key %s' % repr(hkey)) + return None + + debug('find_sdk_dir(): Trying SDK Dir: %s'%sdk_dir) + + if not os.path.exists(sdk_dir): + debug('find_sdk_dir(): %s not on file system' % sdk_dir) + return None + + ftc = os.path.join(sdk_dir, self.sanity_check_file) + if not os.path.exists(ftc): + debug("find_sdk_dir(): sanity check %s not found" % ftc) + return None + + return sdk_dir + + def get_sdk_dir(self): + """Return the MSSSDK given the version string.""" + try: + return self._sdk_dir + except AttributeError: + sdk_dir = self.find_sdk_dir() + self._sdk_dir = sdk_dir + return sdk_dir + + def get_sdk_vc_script(self,host_arch, target_arch): + """ Return the script to initialize the VC compiler installed by SDK + """ + + if (host_arch == 'amd64' and target_arch == 'x86'): + # No cross tools needed compiling 32 bits on 64 bit machine + host_arch=target_arch + + arch_string=target_arch + if (host_arch != target_arch): + arch_string='%s_%s'%(host_arch,target_arch) + + debug("sdk.py: get_sdk_vc_script():arch_string:%s host_arch:%s target_arch:%s"%(arch_string, + host_arch, + target_arch)) + file=self.vc_setup_scripts.get(arch_string,None) + debug("sdk.py: get_sdk_vc_script():file:%s"%file) + return file + +class WindowsSDK(SDKDefinition): + """ + A subclass for trying to find installed Windows SDK directories. + """ + HKEY_FMT = r'Software\Microsoft\Microsoft SDKs\Windows\v%s\InstallationFolder' + def __init__(self, *args, **kw): + SDKDefinition.__init__(self, *args, **kw) + self.hkey_data = self.version + +class PlatformSDK(SDKDefinition): + """ + A subclass for trying to find installed Platform SDK directories. + """ + HKEY_FMT = r'Software\Microsoft\MicrosoftSDK\InstalledSDKS\%s\Install Dir' + def __init__(self, *args, **kw): + SDKDefinition.__init__(self, *args, **kw) + self.hkey_data = self.uuid + +# +# The list of VC initialization scripts installed by the SDK +# These should be tried if the vcvarsall.bat TARGET_ARCH fails +preSDK61VCSetupScripts = { 'x86' : r'bin\vcvars32.bat', + 'amd64' : r'bin\vcvarsamd64.bat', + 'x86_amd64': r'bin\vcvarsx86_amd64.bat', + 'x86_ia64' : r'bin\vcvarsx86_ia64.bat', + 'ia64' : r'bin\vcvarsia64.bat'} + +SDK61VCSetupScripts = {'x86' : r'bin\vcvars32.bat', + 'amd64' : r'bin\amd64\vcvarsamd64.bat', + 'x86_amd64': r'bin\x86_amd64\vcvarsx86_amd64.bat', + 'x86_ia64' : r'bin\x86_ia64\vcvarsx86_ia64.bat', + 'ia64' : r'bin\ia64\vcvarsia64.bat'} + +SDK70VCSetupScripts = { 'x86' : r'bin\vcvars32.bat', + 'amd64' : r'bin\vcvars64.bat', + 'x86_amd64': r'bin\vcvarsx86_amd64.bat', + 'x86_ia64' : r'bin\vcvarsx86_ia64.bat', + 'ia64' : r'bin\vcvarsia64.bat'} + +# The list of support SDKs which we know how to detect. +# +# The first SDK found in the list is the one used by default if there +# are multiple SDKs installed. Barring good reasons to the contrary, +# this means we should list SDKs with from most recent to oldest. +# +# If you update this list, update the documentation in Tool/mssdk.xml. +SupportedSDKList = [ + WindowsSDK('7.0', + sanity_check_file=r'bin\SetEnv.Cmd', + include_subdir='include', + lib_subdir={ + 'x86' : ['lib'], + 'x86_64' : [r'lib\x64'], + 'ia64' : [r'lib\ia64'], + }, + vc_setup_scripts = SDK70VCSetupScripts, + ), + WindowsSDK('6.1', + sanity_check_file=r'bin\SetEnv.Cmd', + include_subdir='include', + lib_subdir={ + 'x86' : ['lib'], + 'x86_64' : [r'lib\x64'], + 'ia64' : [r'lib\ia64'], + }, + vc_setup_scripts = SDK61VCSetupScripts, + ), + + WindowsSDK('6.0A', + sanity_check_file=r'include\windows.h', + include_subdir='include', + lib_subdir={ + 'x86' : ['lib'], + 'x86_64' : [r'lib\x64'], + 'ia64' : [r'lib\ia64'], + }, + vc_setup_scripts = preSDK61VCSetupScripts, + ), + + WindowsSDK('6.0', + sanity_check_file=r'bin\gacutil.exe', + include_subdir='include', + lib_subdir='lib', + vc_setup_scripts = preSDK61VCSetupScripts, + ), + + PlatformSDK('2003R2', + sanity_check_file=r'SetEnv.Cmd', + uuid="D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1", + vc_setup_scripts = preSDK61VCSetupScripts, + ), + + PlatformSDK('2003R1', + sanity_check_file=r'SetEnv.Cmd', + uuid="8F9E5EF3-A9A5-491B-A889-C58EFFECE8B3", + vc_setup_scripts = preSDK61VCSetupScripts, + ), +] + +SupportedSDKMap = {} +for sdk in SupportedSDKList: + SupportedSDKMap[sdk.version] = sdk + + +# Finding installed SDKs isn't cheap, because it goes not only to the +# registry but also to the disk to sanity-check that there is, in fact, +# an SDK installed there and that the registry entry isn't just stale. +# Find this information once, when requested, and cache it. + +InstalledSDKList = None +InstalledSDKMap = None + +def get_installed_sdks(): + global InstalledSDKList + global InstalledSDKMap + debug('sdk.py:get_installed_sdks()') + if InstalledSDKList is None: + InstalledSDKList = [] + InstalledSDKMap = {} + for sdk in SupportedSDKList: + debug('MSCommon/sdk.py: trying to find SDK %s' % sdk.version) + if sdk.get_sdk_dir(): + debug('MSCommon/sdk.py:found SDK %s' % sdk.version) + InstalledSDKList.append(sdk) + InstalledSDKMap[sdk.version] = sdk + return InstalledSDKList + + +# We may be asked to update multiple construction environments with +# SDK information. When doing this, we check on-disk for whether +# the SDK has 'mfc' and 'atl' subdirectories. Since going to disk +# is expensive, cache results by directory. + +SDKEnvironmentUpdates = {} + +def set_sdk_by_directory(env, sdk_dir): + global SDKEnvironmentUpdates + debug('set_sdk_by_directory: Using dir:%s'%sdk_dir) + try: + env_tuple_list = SDKEnvironmentUpdates[sdk_dir] + except KeyError: + env_tuple_list = [] + SDKEnvironmentUpdates[sdk_dir] = env_tuple_list + + include_path = os.path.join(sdk_dir, 'include') + mfc_path = os.path.join(include_path, 'mfc') + atl_path = os.path.join(include_path, 'atl') + + if os.path.exists(mfc_path): + env_tuple_list.append(('INCLUDE', mfc_path)) + if os.path.exists(atl_path): + env_tuple_list.append(('INCLUDE', atl_path)) + env_tuple_list.append(('INCLUDE', include_path)) + + env_tuple_list.append(('LIB', os.path.join(sdk_dir, 'lib'))) + env_tuple_list.append(('LIBPATH', os.path.join(sdk_dir, 'lib'))) + env_tuple_list.append(('PATH', os.path.join(sdk_dir, 'bin'))) + + for variable, directory in env_tuple_list: + env.PrependENVPath(variable, directory) + + +# TODO(sgk): currently unused; remove? +def get_cur_sdk_dir_from_reg(): + """Try to find the platform sdk directory from the registry. + + Return None if failed or the directory does not exist""" + if not SCons.Util.can_read_reg: + debug('SCons cannot read registry') + return None + + try: + val = common.read_reg(_CURINSTALLED_SDK_HKEY_ROOT) + debug("Found current sdk dir in registry: %s" % val) + except WindowsError, e: + debug("Did not find current sdk in registry") + return None + + if not os.path.exists(val): + debug("Current sdk dir %s not on fs" % val) + return None + + return val + +def get_sdk_by_version(mssdk): + if mssdk not in SupportedSDKMap: + msg = "SDK version %s is not supported" % repr(mssdk) + raise SCons.Errors.UserError(msg) + get_installed_sdks() + return InstalledSDKMap.get(mssdk) + +def get_default_sdk(): + """Set up the default Platform/Windows SDK.""" + get_installed_sdks() + if not InstalledSDKList: + return None + return InstalledSDKList[0] + + + + +def mssdk_setup_env(env): + debug('sdk.py:mssdk_setup_env()') + if 'MSSDK_DIR' in env: + sdk_dir = env['MSSDK_DIR'] + if sdk_dir is None: + return + sdk_dir = env.subst(sdk_dir) + debug('sdk.py:mssdk_setup_env: Using MSSDK_DIR:%s'%sdk_dir) + elif 'MSSDK_VERSION' in env: + sdk_version = env['MSSDK_VERSION'] + if sdk_version is None: + msg = "SDK version %s is not installed" % repr(mssdk) + raise SCons.Errors.UserError(msg) + sdk_version = env.subst(sdk_version) + mssdk = get_sdk_by_version(sdk_version) + sdk_dir = mssdk.get_sdk_dir() + debug('sdk.py:mssdk_setup_env: Using MSSDK_VERSION:%s'%sdk_dir) + elif 'MSVS_VERSION' in env: + msvs_version = env['MSVS_VERSION'] + debug('sdk.py:mssdk_setup_env:Getting MSVS_VERSION from env:%s'%msvs_version) + if msvs_version is None: + debug('sdk.py:mssdk_setup_env thinks msvs_version is None') + return + msvs_version = env.subst(msvs_version) + import vs + msvs = vs.get_vs_by_version(msvs_version) + debug('sdk.py:mssdk_setup_env:msvs is :%s'%msvs) + if not msvs: + debug('sdk.py:mssdk_setup_env: no VS version detected, bailingout:%s'%msvs) + return + sdk_version = msvs.sdk_version + debug('sdk.py:msvs.sdk_version is %s'%sdk_version) + if not sdk_version: + return + mssdk = get_sdk_by_version(sdk_version) + if not mssdk: + mssdk = get_default_sdk() + if not mssdk: + return + sdk_dir = mssdk.get_sdk_dir() + debug('sdk.py:mssdk_setup_env: Using MSVS_VERSION:%s'%sdk_dir) + else: + mssdk = get_default_sdk() + if not mssdk: + return + sdk_dir = mssdk.get_sdk_dir() + debug('sdk.py:mssdk_setup_env: not using any env values. sdk_dir:%s'%sdk_dir) + + set_sdk_by_directory(env, sdk_dir) + + #print "No MSVS_VERSION: this is likely to be a bug" + +def mssdk_exists(version=None): + sdks = get_installed_sdks() + if version is None: + return len(sdks) > 0 + return version in sdks + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/MSCommon/vc.py b/scons/scons-local-2.3.0/SCons/Tool/MSCommon/vc.py new file mode 100644 index 000000000..ec285c83b --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/MSCommon/vc.py @@ -0,0 +1,464 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +# TODO: +# * supported arch for versions: for old versions of batch file without +# argument, giving bogus argument cannot be detected, so we have to hardcode +# this here +# * print warning when msvc version specified but not found +# * find out why warning do not print +# * test on 64 bits XP + VS 2005 (and VS 6 if possible) +# * SDK +# * Assembly +__revision__ = "src/engine/SCons/Tool/MSCommon/vc.py 2013/03/03 09:48:35 garyo" + +__doc__ = """Module for Visual C/C++ detection and configuration. +""" +import SCons.compat + +import os +import platform +from string import digits as string_digits + +import SCons.Warnings + +import common + +debug = common.debug + +import sdk + +get_installed_sdks = sdk.get_installed_sdks + + +class VisualCException(Exception): + pass + +class UnsupportedVersion(VisualCException): + pass + +class UnsupportedArch(VisualCException): + pass + +class MissingConfiguration(VisualCException): + pass + +class NoVersionFound(VisualCException): + pass + +class BatchFileExecutionError(VisualCException): + pass + +# Dict to 'canonalize' the arch +_ARCH_TO_CANONICAL = { + "amd64" : "amd64", + "emt64" : "amd64", + "i386" : "x86", + "i486" : "x86", + "i586" : "x86", + "i686" : "x86", + "ia64" : "ia64", + "itanium" : "ia64", + "x86" : "x86", + "x86_64" : "amd64", +} + +# Given a (host, target) tuple, return the argument for the bat file. Both host +# and targets should be canonalized. +_HOST_TARGET_ARCH_TO_BAT_ARCH = { + ("x86", "x86"): "x86", + ("x86", "amd64"): "x86_amd64", + ("amd64", "amd64"): "amd64", + ("amd64", "x86"): "x86", + ("x86", "ia64"): "x86_ia64" +} + +def get_host_target(env): + debug('vc.py:get_host_target()') + + host_platform = env.get('HOST_ARCH') + if not host_platform: + host_platform = platform.machine() + # TODO(2.5): the native Python platform.machine() function returns + # '' on all Python versions before 2.6, after which it also uses + # PROCESSOR_ARCHITECTURE. + if not host_platform: + host_platform = os.environ.get('PROCESSOR_ARCHITECTURE', '') + + # Retain user requested TARGET_ARCH + req_target_platform = env.get('TARGET_ARCH') + debug('vc.py:get_host_target() req_target_platform:%s'%req_target_platform) + + if req_target_platform: + # If user requested a specific platform then only try that one. + target_platform = req_target_platform + else: + target_platform = host_platform + + try: + host = _ARCH_TO_CANONICAL[host_platform.lower()] + except KeyError, e: + msg = "Unrecognized host architecture %s" + raise ValueError(msg % repr(host_platform)) + + try: + target = _ARCH_TO_CANONICAL[target_platform.lower()] + except KeyError, e: + all_archs = str(_ARCH_TO_CANONICAL.keys()) + raise ValueError("Unrecognized target architecture %s\n\tValid architectures: %s" % (target_platform, all_archs)) + + return (host, target,req_target_platform) + +_VCVER = ["11.0", "11.0Exp", "10.0", "10.0Exp", "9.0", "9.0Exp","8.0", "8.0Exp","7.1", "7.0", "6.0"] + +_VCVER_TO_PRODUCT_DIR = { + '11.0': [ + r'Microsoft\VisualStudio\11.0\Setup\VC\ProductDir'], + '11.0Exp' : [ + r'Microsoft\VCExpress\11.0\Setup\VC\ProductDir'], + '10.0': [ + r'Microsoft\VisualStudio\10.0\Setup\VC\ProductDir'], + '10.0Exp' : [ + r'Microsoft\VCExpress\10.0\Setup\VC\ProductDir'], + '9.0': [ + r'Microsoft\VisualStudio\9.0\Setup\VC\ProductDir'], + '9.0Exp' : [ + r'Microsoft\VCExpress\9.0\Setup\VC\ProductDir'], + '8.0': [ + r'Microsoft\VisualStudio\8.0\Setup\VC\ProductDir'], + '8.0Exp': [ + r'Microsoft\VCExpress\8.0\Setup\VC\ProductDir'], + '7.1': [ + r'Microsoft\VisualStudio\7.1\Setup\VC\ProductDir'], + '7.0': [ + r'Microsoft\VisualStudio\7.0\Setup\VC\ProductDir'], + '6.0': [ + r'Microsoft\VisualStudio\6.0\Setup\Microsoft Visual C++\ProductDir'] +} + +def msvc_version_to_maj_min(msvc_version): + msvc_version_numeric = ''.join([x for x in msvc_version if x in string_digits + '.']) + + t = msvc_version_numeric.split(".") + if not len(t) == 2: + raise ValueError("Unrecognized version %s (%s)" % (msvc_version,msvc_version_numeric)) + try: + maj = int(t[0]) + min = int(t[1]) + return maj, min + except ValueError, e: + raise ValueError("Unrecognized version %s (%s)" % (msvc_version,msvc_version_numeric)) + +def is_host_target_supported(host_target, msvc_version): + """Return True if the given (host, target) tuple is supported given the + msvc version. + + Parameters + ---------- + host_target: tuple + tuple of (canonalized) host-target, e.g. ("x86", "amd64") for cross + compilation from 32 bits windows to 64 bits. + msvc_version: str + msvc version (major.minor, e.g. 10.0) + + Note + ---- + This only check whether a given version *may* support the given (host, + target), not that the toolchain is actually present on the machine. + """ + # We assume that any Visual Studio version supports x86 as a target + if host_target[1] != "x86": + maj, min = msvc_version_to_maj_min(msvc_version) + if maj < 8: + return False + + return True + +def find_vc_pdir(msvc_version): + """Try to find the product directory for the given + version. + + Note + ---- + If for some reason the requested version could not be found, an + exception which inherits from VisualCException will be raised.""" + root = 'Software\\' + if common.is_win64(): + root = root + 'Wow6432Node\\' + try: + hkeys = _VCVER_TO_PRODUCT_DIR[msvc_version] + except KeyError: + debug("Unknown version of MSVC: %s" % msvc_version) + raise UnsupportedVersion("Unknown version %s" % msvc_version) + + for key in hkeys: + key = root + key + try: + comps = common.read_reg(key) + except WindowsError, e: + debug('find_vc_dir(): no VC registry key %s' % repr(key)) + else: + debug('find_vc_dir(): found VC in registry: %s' % comps) + if os.path.exists(comps): + return comps + else: + debug('find_vc_dir(): reg says dir is %s, but it does not exist. (ignoring)'\ + % comps) + raise MissingConfiguration("registry dir %s not found on the filesystem" % comps) + return None + +def find_batch_file(env,msvc_version,host_arch,target_arch): + """ + Find the location of the batch script which should set up the compiler + for any TARGET_ARCH whose compilers were installed by Visual Studio/VCExpress + """ + pdir = find_vc_pdir(msvc_version) + if pdir is None: + raise NoVersionFound("No version of Visual Studio found") + + debug('vc.py: find_batch_file() pdir:%s'%pdir) + + # filter out e.g. "Exp" from the version name + msvc_ver_numeric = ''.join([x for x in msvc_version if x in string_digits + "."]) + vernum = float(msvc_ver_numeric) + if 7 <= vernum < 8: + pdir = os.path.join(pdir, os.pardir, "Common7", "Tools") + batfilename = os.path.join(pdir, "vsvars32.bat") + elif vernum < 7: + pdir = os.path.join(pdir, "Bin") + batfilename = os.path.join(pdir, "vcvars32.bat") + else: # >= 8 + batfilename = os.path.join(pdir, "vcvarsall.bat") + + if not os.path.exists(batfilename): + debug("Not found: %s" % batfilename) + batfilename = None + + installed_sdks=get_installed_sdks() + for _sdk in installed_sdks: + sdk_bat_file=_sdk.get_sdk_vc_script(host_arch,target_arch) + sdk_bat_file_path=os.path.join(pdir,sdk_bat_file) + debug('vc.py:find_batch_file() sdk_bat_file_path:%s'%sdk_bat_file_path) + if os.path.exists(sdk_bat_file_path): + return (batfilename,sdk_bat_file_path) + else: + debug("vc.py:find_batch_file() not found:%s"%sdk_bat_file_path) + else: + return (batfilename,None) + +__INSTALLED_VCS_RUN = None + +def cached_get_installed_vcs(): + global __INSTALLED_VCS_RUN + + if __INSTALLED_VCS_RUN is None: + ret = get_installed_vcs() + __INSTALLED_VCS_RUN = ret + + return __INSTALLED_VCS_RUN + +def get_installed_vcs(): + installed_versions = [] + for ver in _VCVER: + debug('trying to find VC %s' % ver) + try: + if find_vc_pdir(ver): + debug('found VC %s' % ver) + installed_versions.append(ver) + else: + debug('find_vc_pdir return None for ver %s' % ver) + except VisualCException, e: + debug('did not find VC %s: caught exception %s' % (ver, str(e))) + return installed_versions + +def reset_installed_vcs(): + """Make it try again to find VC. This is just for the tests.""" + __INSTALLED_VCS_RUN = None + +def script_env(script, args=None): + stdout = common.get_output(script, args) + # Stupid batch files do not set return code: we take a look at the + # beginning of the output for an error message instead + olines = stdout.splitlines() + if olines[0].startswith("The specified configuration type is missing"): + raise BatchFileExecutionError("\n".join(olines[:2])) + + return common.parse_output(stdout) + +def get_default_version(env): + debug('get_default_version()') + + msvc_version = env.get('MSVC_VERSION') + msvs_version = env.get('MSVS_VERSION') + + debug('get_default_version(): msvc_version:%s msvs_version:%s'%(msvc_version,msvs_version)) + + if msvs_version and not msvc_version: + SCons.Warnings.warn( + SCons.Warnings.DeprecatedWarning, + "MSVS_VERSION is deprecated: please use MSVC_VERSION instead ") + return msvs_version + elif msvc_version and msvs_version: + if not msvc_version == msvs_version: + SCons.Warnings.warn( + SCons.Warnings.VisualVersionMismatch, + "Requested msvc version (%s) and msvs version (%s) do " \ + "not match: please use MSVC_VERSION only to request a " \ + "visual studio version, MSVS_VERSION is deprecated" \ + % (msvc_version, msvs_version)) + return msvs_version + if not msvc_version: + installed_vcs = cached_get_installed_vcs() + debug('installed_vcs:%s' % installed_vcs) + if not installed_vcs: + #msg = 'No installed VCs' + #debug('msv %s\n' % repr(msg)) + #SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg) + debug('msvc_setup_env: No installed VCs') + return None + msvc_version = installed_vcs[0] + debug('msvc_setup_env: using default installed MSVC version %s\n' % repr(msvc_version)) + + return msvc_version + +def msvc_setup_env_once(env): + try: + has_run = env["MSVC_SETUP_RUN"] + except KeyError: + has_run = False + + if not has_run: + msvc_setup_env(env) + env["MSVC_SETUP_RUN"] = True + +def msvc_find_valid_batch_script(env,version): + debug('vc.py:msvc_find_valid_batch_script()') + # Find the host platform, target platform, and if present the requested + # target platform + (host_platform, target_platform,req_target_platform) = get_host_target(env) + + # If the user hasn't specifically requested a TARGET_ARCH, and + # The TARGET_ARCH is amd64 then also try 32 bits if there are no viable + # 64 bit tools installed + try_target_archs = [target_platform] + if not req_target_platform and target_platform in ('amd64','x86_64'): + try_target_archs.append('x86') + + d = None + for tp in try_target_archs: + # Set to current arch. + env['TARGET_ARCH']=tp + + debug("vc.py:msvc_find_valid_batch_script() trying target_platform:%s"%tp) + host_target = (host_platform, tp) + if not is_host_target_supported(host_target, version): + warn_msg = "host, target = %s not supported for MSVC version %s" % \ + (host_target, version) + SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) + arg = _HOST_TARGET_ARCH_TO_BAT_ARCH[host_target] + + # Try to locate a batch file for this host/target platform combo + try: + (vc_script,sdk_script) = find_batch_file(env,version,host_platform,tp) + debug('vc.py:msvc_find_valid_batch_script() vc_script:%s sdk_script:%s'%(vc_script,sdk_script)) + except VisualCException, e: + msg = str(e) + debug('Caught exception while looking for batch file (%s)' % msg) + warn_msg = "VC version %s not installed. " + \ + "C/C++ compilers are most likely not set correctly.\n" + \ + " Installed versions are: %s" + warn_msg = warn_msg % (version, cached_get_installed_vcs()) + SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) + continue + + # Try to use the located batch file for this host/target platform combo + debug('vc.py:msvc_find_valid_batch_script() use_script 2 %s, args:%s\n' % (repr(vc_script), arg)) + if vc_script: + try: + d = script_env(vc_script, args=arg) + except BatchFileExecutionError, e: + debug('vc.py:msvc_find_valid_batch_script() use_script 3: failed running VC script %s: %s: Error:%s'%(repr(vc_script),arg,e)) + vc_script=None + if not vc_script and sdk_script: + debug('vc.py:msvc_find_valid_batch_script() use_script 4: trying sdk script: %s'%(sdk_script)) + try: + d = script_env(sdk_script,args=[]) + except BatchFileExecutionError,e: + debug('vc.py:msvc_find_valid_batch_script() use_script 5: failed running SDK script %s: Error:%s'%(repr(sdk_script),e)) + continue + elif not vc_script and not sdk_script: + debug('vc.py:msvc_find_valid_batch_script() use_script 6: Neither VC script nor SDK script found') + continue + + # If we cannot find a viable installed compiler, reset the TARGET_ARCH + # To it's initial value + if not d: + env['TARGET_ARCH']=req_target_platform + + return d + + +def msvc_setup_env(env): + debug('msvc_setup_env()') + + version = get_default_version(env) + if version is None: + warn_msg = "No version of Visual Studio compiler found - C/C++ " \ + "compilers most likely not set correctly" + SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) + return None + debug('msvc_setup_env: using specified MSVC version %s\n' % repr(version)) + + # XXX: we set-up both MSVS version for backward + # compatibility with the msvs tool + env['MSVC_VERSION'] = version + env['MSVS_VERSION'] = version + env['MSVS'] = {} + + + use_script = env.get('MSVC_USE_SCRIPT', True) + if SCons.Util.is_String(use_script): + debug('vc.py:msvc_setup_env() use_script 1 %s\n' % repr(use_script)) + d = script_env(use_script) + elif use_script: + d = msvc_find_valid_batch_script(env,version) + debug('vc.py:msvc_setup_env() use_script 2 %s\n' % d) + if not d: + return d + else: + debug('MSVC_USE_SCRIPT set to False') + warn_msg = "MSVC_USE_SCRIPT set to False, assuming environment " \ + "set correctly." + SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) + return None + + for k, v in d.items(): + debug('vc.py:msvc_setup_env() env:%s -> %s'%(k,v)) + env.PrependENVPath(k, v, delete_existing=True) + +def msvc_exists(version=None): + vcs = cached_get_installed_vcs() + if version is None: + return len(vcs) > 0 + return version in vcs + diff --git a/scons/scons-local-2.3.0/SCons/Tool/MSCommon/vs.py b/scons/scons-local-2.3.0/SCons/Tool/MSCommon/vs.py new file mode 100644 index 000000000..18b31a0be --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/MSCommon/vs.py @@ -0,0 +1,553 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/MSCommon/vs.py 2013/03/03 09:48:35 garyo" + +__doc__ = """Module to detect Visual Studio and/or Visual C/C++ +""" + +import os + +import SCons.Errors +import SCons.Util + +from common import debug, \ + get_output, \ + is_win64, \ + normalize_env, \ + parse_output, \ + read_reg + +import SCons.Tool.MSCommon.vc + +class VisualStudio(object): + """ + An abstract base class for trying to find installed versions of + Visual Studio. + """ + def __init__(self, version, **kw): + self.version = version + kw['vc_version'] = kw.get('vc_version', version) + kw['sdk_version'] = kw.get('sdk_version', version) + self.__dict__.update(kw) + self._cache = {} + + # + + def find_batch_file(self): + vs_dir = self.get_vs_dir() + if not vs_dir: + debug('find_executable(): no vs_dir') + return None + batch_file = os.path.join(vs_dir, self.batch_file_path) + batch_file = os.path.normpath(batch_file) + if not os.path.isfile(batch_file): + debug('find_batch_file(): %s not on file system' % batch_file) + return None + return batch_file + + def find_vs_dir_by_vc(self): + SCons.Tool.MSCommon.vc.get_installed_vcs() + dir = SCons.Tool.MSCommon.vc.find_vc_pdir(self.vc_version) + if not dir: + debug('find_vs_dir(): no installed VC %s' % self.vc_version) + return None + return dir + + def find_vs_dir_by_reg(self): + root = 'Software\\' + + if is_win64(): + root = root + 'Wow6432Node\\' + for key in self.hkeys: + if key=='use_dir': + return self.find_vs_dir_by_vc() + key = root + key + try: + comps = read_reg(key) + except WindowsError, e: + debug('find_vs_dir_by_reg(): no VS registry key %s' % repr(key)) + else: + debug('find_vs_dir_by_reg(): found VS in registry: %s' % comps) + return comps + return None + + def find_vs_dir(self): + """ Can use registry or location of VC to find vs dir + First try to find by registry, and if that fails find via VC dir + """ + + + if True: + vs_dir=self.find_vs_dir_by_reg() + return vs_dir + else: + return self.find_vs_dir_by_vc() + + def find_executable(self): + vs_dir = self.get_vs_dir() + if not vs_dir: + debug('find_executable(): no vs_dir (%s)'%vs_dir) + return None + executable = os.path.join(vs_dir, self.executable_path) + executable = os.path.normpath(executable) + if not os.path.isfile(executable): + debug('find_executable(): %s not on file system' % executable) + return None + return executable + + # + + def get_batch_file(self): + try: + return self._cache['batch_file'] + except KeyError: + batch_file = self.find_batch_file() + self._cache['batch_file'] = batch_file + return batch_file + + def get_executable(self): + try: + debug('get_executable using cache:%s'%self._cache['executable']) + return self._cache['executable'] + except KeyError: + executable = self.find_executable() + self._cache['executable'] = executable + debug('get_executable not in cache:%s'%executable) + return executable + + def get_vs_dir(self): + try: + return self._cache['vs_dir'] + except KeyError: + vs_dir = self.find_vs_dir() + self._cache['vs_dir'] = vs_dir + return vs_dir + + def get_supported_arch(self): + try: + return self._cache['supported_arch'] + except KeyError: + # RDEVE: for the time being use hardcoded lists + # supported_arch = self.find_supported_arch() + self._cache['supported_arch'] = self.supported_arch + return self.supported_arch + + def reset(self): + self._cache = {} + +# The list of supported Visual Studio versions we know how to detect. +# +# How to look for .bat file ? +# - VS 2008 Express (x86): +# * from registry key productdir, gives the full path to vsvarsall.bat. In +# HKEY_LOCAL_MACHINE): +# Software\Microsoft\VCEpress\9.0\Setup\VC\productdir +# * from environmnent variable VS90COMNTOOLS: the path is then ..\..\VC +# relatively to the path given by the variable. +# +# - VS 2008 Express (WoW6432: 32 bits on windows x64): +# Software\Wow6432Node\Microsoft\VCEpress\9.0\Setup\VC\productdir +# +# - VS 2005 Express (x86): +# * from registry key productdir, gives the full path to vsvarsall.bat. In +# HKEY_LOCAL_MACHINE): +# Software\Microsoft\VCEpress\8.0\Setup\VC\productdir +# * from environmnent variable VS80COMNTOOLS: the path is then ..\..\VC +# relatively to the path given by the variable. +# +# - VS 2005 Express (WoW6432: 32 bits on windows x64): does not seem to have a +# productdir ? +# +# - VS 2003 .Net (pro edition ? x86): +# * from registry key productdir. The path is then ..\Common7\Tools\ +# relatively to the key. The key is in HKEY_LOCAL_MACHINE): +# Software\Microsoft\VisualStudio\7.1\Setup\VC\productdir +# * from environmnent variable VS71COMNTOOLS: the path is the full path to +# vsvars32.bat +# +# - VS 98 (VS 6): +# * from registry key productdir. The path is then Bin +# relatively to the key. The key is in HKEY_LOCAL_MACHINE): +# Software\Microsoft\VisualStudio\6.0\Setup\VC98\productdir +# +# The first version found in the list is the one used by default if +# there are multiple versions installed. Barring good reasons to +# the contrary, this means we should list versions from most recent +# to oldest. Pro versions get listed before Express versions on the +# assumption that, by default, you'd rather use the version you paid +# good money for in preference to whatever Microsoft makes available +# for free. +# +# If you update this list, update the documentation in Tool/msvs.xml. + +SupportedVSList = [ + # Visual Studio 2010 + # TODO: find the settings, perhaps from someone with a CTP copy? + #VisualStudio('TBD', + # hkey_root=r'TBD', + # common_tools_var='TBD', + # executable_path=r'TBD', + # default_dirname='TBD', + #), + + # Visual Studio 11 + # The batch file we look for is in the VC directory, + # so the devenv.com executable is up in ..\..\Common7\IDE. + VisualStudio('11.0', + sdk_version='6.1', + hkeys=[r'Microsoft\VisualStudio\11.0\Setup\VS\ProductDir'], + common_tools_var='VS110COMNTOOLS', + executable_path=r'Common7\IDE\devenv.com', + batch_file_path=r'Common7\Tools\vsvars32.bat', + default_dirname='Microsoft Visual Studio 11', + supported_arch=['x86', 'amd64'], + ), + + # Visual C++ 11 Express Edition + # The batch file we look for is in the VC directory, + # so the VCExpress.exe executable is up in ..\..\Common7\IDE. + VisualStudio('11.0Exp', + vc_version='11.0', + sdk_version='6.1', + hkeys=[r'Microsoft\VCExpress\11.0\Setup\VS\ProductDir'], + common_tools_var='VS110COMNTOOLS', + executable_path=r'Common7\IDE\VCExpress.exe', + batch_file_path=r'Common7\Tools\vsvars32.bat', + default_dirname='Microsoft Visual Studio 11', + supported_arch=['x86'], + ), + + # Visual Studio 2010 + # The batch file we look for is in the VC directory, + # so the devenv.com executable is up in ..\..\Common7\IDE. + VisualStudio('10.0', + sdk_version='6.1', + hkeys=[r'Microsoft\VisualStudio\10.0\Setup\VS\ProductDir'], + common_tools_var='VS100COMNTOOLS', + executable_path=r'Common7\IDE\devenv.com', + batch_file_path=r'Common7\Tools\vsvars32.bat', + default_dirname='Microsoft Visual Studio 10', + supported_arch=['x86', 'amd64'], + ), + + # Visual C++ 2010 Express Edition + # The batch file we look for is in the VC directory, + # so the VCExpress.exe executable is up in ..\..\Common7\IDE. + VisualStudio('10.0Exp', + vc_version='10.0', + sdk_version='6.1', + hkeys=[r'Microsoft\VCExpress\10.0\Setup\VS\ProductDir'], + common_tools_var='VS100COMNTOOLS', + executable_path=r'Common7\IDE\VCExpress.exe', + batch_file_path=r'Common7\Tools\vsvars32.bat', + default_dirname='Microsoft Visual Studio 10', + supported_arch=['x86'], + ), + + # Visual Studio 2008 + # The batch file we look for is in the VC directory, + # so the devenv.com executable is up in ..\..\Common7\IDE. + VisualStudio('9.0', + sdk_version='6.1', + hkeys=[r'Microsoft\VisualStudio\9.0\Setup\VS\ProductDir'], + common_tools_var='VS90COMNTOOLS', + executable_path=r'Common7\IDE\devenv.com', + batch_file_path=r'Common7\Tools\vsvars32.bat', + default_dirname='Microsoft Visual Studio 9', + supported_arch=['x86', 'amd64'], + ), + + # Visual C++ 2008 Express Edition + # The batch file we look for is in the VC directory, + # so the VCExpress.exe executable is up in ..\..\Common7\IDE. + VisualStudio('9.0Exp', + vc_version='9.0', + sdk_version='6.1', + hkeys=[r'Microsoft\VCExpress\9.0\Setup\VS\ProductDir'], + common_tools_var='VS90COMNTOOLS', + executable_path=r'Common7\IDE\VCExpress.exe', + batch_file_path=r'Common7\Tools\vsvars32.bat', + default_dirname='Microsoft Visual Studio 9', + supported_arch=['x86'], + ), + + # Visual Studio 2005 + # The batch file we look for is in the VC directory, + # so the devenv.com executable is up in ..\..\Common7\IDE. + VisualStudio('8.0', + sdk_version='6.0A', + hkeys=[r'Microsoft\VisualStudio\8.0\Setup\VS\ProductDir'], + common_tools_var='VS80COMNTOOLS', + executable_path=r'Common7\IDE\devenv.com', + batch_file_path=r'Common7\Tools\vsvars32.bat', + default_dirname='Microsoft Visual Studio 8', + supported_arch=['x86', 'amd64'], + ), + + # Visual C++ 2005 Express Edition + # The batch file we look for is in the VC directory, + # so the VCExpress.exe executable is up in ..\..\Common7\IDE. + VisualStudio('8.0Exp', + vc_version='8.0Exp', + sdk_version='6.0A', + hkeys=[r'Microsoft\VCExpress\8.0\Setup\VS\ProductDir'], + common_tools_var='VS80COMNTOOLS', + executable_path=r'Common7\IDE\VCExpress.exe', + batch_file_path=r'Common7\Tools\vsvars32.bat', + default_dirname='Microsoft Visual Studio 8', + supported_arch=['x86'], + ), + + # Visual Studio .NET 2003 + # The batch file we look for is in the Common7\Tools directory, + # so the devenv.com executable is next door in ..\IDE. + VisualStudio('7.1', + sdk_version='6.0', + hkeys=[r'Microsoft\VisualStudio\7.1\Setup\VS\ProductDir'], + common_tools_var='VS71COMNTOOLS', + executable_path=r'Common7\IDE\devenv.com', + batch_file_path=r'Common7\Tools\vsvars32.bat', + default_dirname='Microsoft Visual Studio .NET 2003', + supported_arch=['x86'], + ), + + # Visual Studio .NET + # The batch file we look for is in the Common7\Tools directory, + # so the devenv.com executable is next door in ..\IDE. + VisualStudio('7.0', + sdk_version='2003R2', + hkeys=[r'Microsoft\VisualStudio\7.0\Setup\VS\ProductDir'], + common_tools_var='VS70COMNTOOLS', + executable_path=r'IDE\devenv.com', + batch_file_path=r'Common7\Tools\vsvars32.bat', + default_dirname='Microsoft Visual Studio .NET', + supported_arch=['x86'], + ), + + # Visual Studio 6.0 + VisualStudio('6.0', + sdk_version='2003R1', + hkeys=[r'Microsoft\VisualStudio\6.0\Setup\Microsoft Visual Studio\ProductDir', + 'use_dir'], + common_tools_var='VS60COMNTOOLS', + executable_path=r'Common\MSDev98\Bin\MSDEV.COM', + batch_file_path=r'Common7\Tools\vsvars32.bat', + default_dirname='Microsoft Visual Studio', + supported_arch=['x86'], + ), +] + +SupportedVSMap = {} +for vs in SupportedVSList: + SupportedVSMap[vs.version] = vs + + +# Finding installed versions of Visual Studio isn't cheap, because it +# goes not only to the registry but also to the disk to sanity-check +# that there is, in fact, a Visual Studio directory there and that the +# registry entry isn't just stale. Find this information once, when +# requested, and cache it. + +InstalledVSList = None +InstalledVSMap = None + +def get_installed_visual_studios(): + global InstalledVSList + global InstalledVSMap + if InstalledVSList is None: + InstalledVSList = [] + InstalledVSMap = {} + for vs in SupportedVSList: + debug('trying to find VS %s' % vs.version) + if vs.get_executable(): + debug('found VS %s' % vs.version) + InstalledVSList.append(vs) + InstalledVSMap[vs.version] = vs + return InstalledVSList + +def reset_installed_visual_studios(): + global InstalledVSList + global InstalledVSMap + InstalledVSList = None + InstalledVSMap = None + for vs in SupportedVSList: + vs.reset() + + # Need to clear installed VC's as well as they are used in finding + # installed VS's + SCons.Tool.MSCommon.vc.reset_installed_vcs() + + +# We may be asked to update multiple construction environments with +# SDK information. When doing this, we check on-disk for whether +# the SDK has 'mfc' and 'atl' subdirectories. Since going to disk +# is expensive, cache results by directory. + +#SDKEnvironmentUpdates = {} +# +#def set_sdk_by_directory(env, sdk_dir): +# global SDKEnvironmentUpdates +# try: +# env_tuple_list = SDKEnvironmentUpdates[sdk_dir] +# except KeyError: +# env_tuple_list = [] +# SDKEnvironmentUpdates[sdk_dir] = env_tuple_list +# +# include_path = os.path.join(sdk_dir, 'include') +# mfc_path = os.path.join(include_path, 'mfc') +# atl_path = os.path.join(include_path, 'atl') +# +# if os.path.exists(mfc_path): +# env_tuple_list.append(('INCLUDE', mfc_path)) +# if os.path.exists(atl_path): +# env_tuple_list.append(('INCLUDE', atl_path)) +# env_tuple_list.append(('INCLUDE', include_path)) +# +# env_tuple_list.append(('LIB', os.path.join(sdk_dir, 'lib'))) +# env_tuple_list.append(('LIBPATH', os.path.join(sdk_dir, 'lib'))) +# env_tuple_list.append(('PATH', os.path.join(sdk_dir, 'bin'))) +# +# for variable, directory in env_tuple_list: +# env.PrependENVPath(variable, directory) + +def msvs_exists(): + return (len(get_installed_visual_studios()) > 0) + +def get_vs_by_version(msvs): + global InstalledVSMap + global SupportedVSMap + + debug('vs.py:get_vs_by_version()') + if msvs not in SupportedVSMap: + msg = "Visual Studio version %s is not supported" % repr(msvs) + raise SCons.Errors.UserError(msg) + get_installed_visual_studios() + vs = InstalledVSMap.get(msvs) + debug('InstalledVSMap:%s'%InstalledVSMap) + debug('vs.py:get_vs_by_version: found vs:%s'%vs) + # Some check like this would let us provide a useful error message + # if they try to set a Visual Studio version that's not installed. + # However, we also want to be able to run tests (like the unit + # tests) on systems that don't, or won't ever, have it installed. + # It might be worth resurrecting this, with some configurable + # setting that the tests can use to bypass the check. + #if not vs: + # msg = "Visual Studio version %s is not installed" % repr(msvs) + # raise SCons.Errors.UserError, msg + return vs + +def get_default_version(env): + """Returns the default version string to use for MSVS. + + If no version was requested by the user through the MSVS environment + variable, query all the available the visual studios through + query_versions, and take the highest one. + + Return + ------ + version: str + the default version. + """ + if 'MSVS' not in env or not SCons.Util.is_Dict(env['MSVS']): + versions = [vs.version for vs in get_installed_visual_studios()] + env['MSVS'] = {'VERSIONS' : versions} + else: + versions = env['MSVS'].get('VERSIONS', []) + + if 'MSVS_VERSION' not in env: + if versions: + env['MSVS_VERSION'] = versions[0] #use highest version by default + else: + env['MSVS_VERSION'] = SupportedVSList[0].version + + env['MSVS']['VERSION'] = env['MSVS_VERSION'] + + return env['MSVS_VERSION'] + +def get_default_arch(env): + """Return the default arch to use for MSVS + + if no version was requested by the user through the MSVS_ARCH environment + variable, select x86 + + Return + ------ + arch: str + """ + arch = env.get('MSVS_ARCH', 'x86') + + msvs = InstalledVSMap.get(env['MSVS_VERSION']) + + if not msvs: + arch = 'x86' + elif not arch in msvs.get_supported_arch(): + fmt = "Visual Studio version %s does not support architecture %s" + raise SCons.Errors.UserError(fmt % (env['MSVS_VERSION'], arch)) + + return arch + +def merge_default_version(env): + version = get_default_version(env) + arch = get_default_arch(env) + +def msvs_setup_env(env): + batfilename = msvs.get_batch_file() + msvs = get_vs_by_version(version) + if msvs is None: + return + + # XXX: I think this is broken. This will silently set a bogus tool instead + # of failing, but there is no other way with the current scons tool + # framework + if batfilename is not None: + + vars = ('LIB', 'LIBPATH', 'PATH', 'INCLUDE') + + msvs_list = get_installed_visual_studios() + vscommonvarnames = [vs.common_tools_var for vs in msvs_list] + save_ENV = env['ENV'] + nenv = normalize_env(env['ENV'], + ['COMSPEC'] + vscommonvarnames, + force=True) + try: + output = get_output(batfilename, arch, env=nenv) + finally: + env['ENV'] = save_ENV + vars = parse_output(output, vars) + + for k, v in vars.items(): + env.PrependENVPath(k, v, delete_existing=1) + +def query_versions(): + """Query the system to get available versions of VS. A version is + considered when a batfile is found.""" + msvs_list = get_installed_visual_studios() + versions = [msvs.version for msvs in msvs_list] + return versions + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/Perforce.py b/scons/scons-local-2.3.0/SCons/Tool/Perforce.py new file mode 100644 index 000000000..3b56fee24 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/Perforce.py @@ -0,0 +1,103 @@ +"""SCons.Tool.Perforce.py + +Tool-specific initialization for Perforce Source Code Management system. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Tool/Perforce.py 2013/03/03 09:48:35 garyo" + +import os + +import SCons.Action +import SCons.Builder +import SCons.Node.FS +import SCons.Util + +# This function should maybe be moved to SCons.Util? +from SCons.Tool.PharLapCommon import addPathIfNotExists + + +# Variables that we want to import from the base OS environment. +_import_env = [ 'P4PORT', 'P4CLIENT', 'P4USER', 'USER', 'USERNAME', 'P4PASSWD', + 'P4CHARSET', 'P4LANGUAGE', 'SystemRoot' ] + +PerforceAction = SCons.Action.Action('$P4COM', '$P4COMSTR') + +def generate(env): + """Add a Builder factory function and construction variables for + Perforce to an Environment.""" + + def PerforceFactory(env=env): + """ """ + import SCons.Warnings as W + W.warn(W.DeprecatedSourceCodeWarning, """The Perforce() factory is deprecated and there is no replacement.""") + return SCons.Builder.Builder(action = PerforceAction, env = env) + + #setattr(env, 'Perforce', PerforceFactory) + env.Perforce = PerforceFactory + + env['P4'] = 'p4' + env['P4FLAGS'] = SCons.Util.CLVar('') + env['P4COM'] = '$P4 $P4FLAGS sync $TARGET' + try: + environ = env['ENV'] + except KeyError: + environ = {} + env['ENV'] = environ + + # Perforce seems to use the PWD environment variable rather than + # calling getcwd() for itself, which is odd. If no PWD variable + # is present, p4 WILL call getcwd, but this seems to cause problems + # with good ol' Windows's tilde-mangling for long file names. + environ['PWD'] = env.Dir('#').get_abspath() + + for var in _import_env: + v = os.environ.get(var) + if v: + environ[var] = v + + if SCons.Util.can_read_reg: + # If we can read the registry, add the path to Perforce to our environment. + try: + k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, + 'Software\\Perforce\\environment') + val, tok = SCons.Util.RegQueryValueEx(k, 'P4INSTROOT') + addPathIfNotExists(environ, 'PATH', val) + except SCons.Util.RegError: + # Can't detect where Perforce is, hope the user has it set in the + # PATH. + pass + +def exists(env): + return env.Detect('p4') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/PharLapCommon.py b/scons/scons-local-2.3.0/SCons/Tool/PharLapCommon.py new file mode 100644 index 000000000..7602073f2 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/PharLapCommon.py @@ -0,0 +1,137 @@ +"""SCons.Tool.PharLapCommon + +This module contains common code used by all Tools for the +Phar Lap ETS tool chain. Right now, this is linkloc and +386asm. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/PharLapCommon.py 2013/03/03 09:48:35 garyo" + +import os +import os.path +import SCons.Errors +import SCons.Util +import re + +def getPharLapPath(): + """Reads the registry to find the installed path of the Phar Lap ETS + development kit. + + Raises UserError if no installed version of Phar Lap can + be found.""" + + if not SCons.Util.can_read_reg: + raise SCons.Errors.InternalError("No Windows registry module was found") + try: + k=SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, + 'SOFTWARE\\Pharlap\\ETS') + val, type = SCons.Util.RegQueryValueEx(k, 'BaseDir') + + # The following is a hack...there is (not surprisingly) + # an odd issue in the Phar Lap plug in that inserts + # a bunch of junk data after the phar lap path in the + # registry. We must trim it. + idx=val.find('\0') + if idx >= 0: + val = val[:idx] + + return os.path.normpath(val) + except SCons.Util.RegError: + raise SCons.Errors.UserError("Cannot find Phar Lap ETS path in the registry. Is it installed properly?") + +REGEX_ETS_VER = re.compile(r'#define\s+ETS_VER\s+([0-9]+)') + +def getPharLapVersion(): + """Returns the version of the installed ETS Tool Suite as a + decimal number. This version comes from the ETS_VER #define in + the embkern.h header. For example, '#define ETS_VER 1010' (which + is what Phar Lap 10.1 defines) would cause this method to return + 1010. Phar Lap 9.1 does not have such a #define, but this method + will return 910 as a default. + + Raises UserError if no installed version of Phar Lap can + be found.""" + + include_path = os.path.join(getPharLapPath(), os.path.normpath("include/embkern.h")) + if not os.path.exists(include_path): + raise SCons.Errors.UserError("Cannot find embkern.h in ETS include directory.\nIs Phar Lap ETS installed properly?") + mo = REGEX_ETS_VER.search(open(include_path, 'r').read()) + if mo: + return int(mo.group(1)) + # Default return for Phar Lap 9.1 + return 910 + +def addPathIfNotExists(env_dict, key, path, sep=os.pathsep): + """This function will take 'key' out of the dictionary + 'env_dict', then add the path 'path' to that key if it is not + already there. This treats the value of env_dict[key] as if it + has a similar format to the PATH variable...a list of paths + separated by tokens. The 'path' will get added to the list if it + is not already there.""" + try: + is_list = 1 + paths = env_dict[key] + if not SCons.Util.is_List(env_dict[key]): + paths = paths.split(sep) + is_list = 0 + if os.path.normcase(path) not in list(map(os.path.normcase, paths)): + paths = [ path ] + paths + if is_list: + env_dict[key] = paths + else: + env_dict[key] = sep.join(paths) + except KeyError: + env_dict[key] = path + +def addPharLapPaths(env): + """This function adds the path to the Phar Lap binaries, includes, + and libraries, if they are not already there.""" + ph_path = getPharLapPath() + + try: + env_dict = env['ENV'] + except KeyError: + env_dict = {} + env['ENV'] = env_dict + addPathIfNotExists(env_dict, 'PATH', + os.path.join(ph_path, 'bin')) + addPathIfNotExists(env_dict, 'INCLUDE', + os.path.join(ph_path, 'include')) + addPathIfNotExists(env_dict, 'LIB', + os.path.join(ph_path, 'lib')) + addPathIfNotExists(env_dict, 'LIB', + os.path.join(ph_path, os.path.normpath('lib/vclib'))) + + env['PHARLAP_PATH'] = getPharLapPath() + env['PHARLAP_VERSION'] = str(getPharLapVersion()) + + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/RCS.py b/scons/scons-local-2.3.0/SCons/Tool/RCS.py new file mode 100644 index 000000000..05505ac8b --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/RCS.py @@ -0,0 +1,64 @@ +"""SCons.Tool.RCS.py + +Tool-specific initialization for RCS. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Tool/RCS.py 2013/03/03 09:48:35 garyo" + +import SCons.Action +import SCons.Builder +import SCons.Util + +def generate(env): + """Add a Builder factory function and construction variables for + RCS to an Environment.""" + + def RCSFactory(env=env): + """ """ + import SCons.Warnings as W + W.warn(W.DeprecatedSourceCodeWarning, """The RCS() factory is deprecated and there is no replacement.""") + act = SCons.Action.Action('$RCS_COCOM', '$RCS_COCOMSTR') + return SCons.Builder.Builder(action = act, env = env) + + #setattr(env, 'RCS', RCSFactory) + env.RCS = RCSFactory + + env['RCS'] = 'rcs' + env['RCS_CO'] = 'co' + env['RCS_COFLAGS'] = SCons.Util.CLVar('') + env['RCS_COCOM'] = '$RCS_CO $RCS_COFLAGS $TARGET' + +def exists(env): + return env.Detect('rcs') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/SCCS.py b/scons/scons-local-2.3.0/SCons/Tool/SCCS.py new file mode 100644 index 000000000..6f795a5cc --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/SCCS.py @@ -0,0 +1,64 @@ +"""SCons.Tool.SCCS.py + +Tool-specific initialization for SCCS. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Tool/SCCS.py 2013/03/03 09:48:35 garyo" + +import SCons.Action +import SCons.Builder +import SCons.Util + +def generate(env): + """Add a Builder factory function and construction variables for + SCCS to an Environment.""" + + def SCCSFactory(env=env): + """ """ + import SCons.Warnings as W + W.warn(W.DeprecatedSourceCodeWarning, """The SCCS() factory is deprecated and there is no replacement.""") + act = SCons.Action.Action('$SCCSCOM', '$SCCSCOMSTR') + return SCons.Builder.Builder(action = act, env = env) + + #setattr(env, 'SCCS', SCCSFactory) + env.SCCS = SCCSFactory + + env['SCCS'] = 'sccs' + env['SCCSFLAGS'] = SCons.Util.CLVar('') + env['SCCSGETFLAGS'] = SCons.Util.CLVar('') + env['SCCSCOM'] = '$SCCS $SCCSFLAGS get $SCCSGETFLAGS $TARGET' + +def exists(env): + return env.Detect('sccs') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/Subversion.py b/scons/scons-local-2.3.0/SCons/Tool/Subversion.py new file mode 100644 index 000000000..41771ad28 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/Subversion.py @@ -0,0 +1,71 @@ +"""SCons.Tool.Subversion.py + +Tool-specific initialization for Subversion. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Tool/Subversion.py 2013/03/03 09:48:35 garyo" + +import os.path + +import SCons.Action +import SCons.Builder +import SCons.Util + +def generate(env): + """Add a Builder factory function and construction variables for + Subversion to an Environment.""" + + def SubversionFactory(repos, module='', env=env): + """ """ + # fail if repos is not an absolute path name? + import SCons.Warnings as W + W.warn(W.DeprecatedSourceCodeWarning, """The Subversion() factory is deprecated and there is no replacement.""") + if module != '': + module = os.path.join(module, '') + act = SCons.Action.Action('$SVNCOM', '$SVNCOMSTR') + return SCons.Builder.Builder(action = act, + env = env, + SVNREPOSITORY = repos, + SVNMODULE = module) + + #setattr(env, 'Subversion', SubversionFactory) + env.Subversion = SubversionFactory + + env['SVN'] = 'svn' + env['SVNFLAGS'] = SCons.Util.CLVar('') + env['SVNCOM'] = '$SVN $SVNFLAGS cat $SVNREPOSITORY/$SVNMODULE$TARGET > $TARGET' + +def exists(env): + return env.Detect('svn') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/__init__.py b/scons/scons-local-2.3.0/SCons/Tool/__init__.py new file mode 100644 index 000000000..b12095f94 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/__init__.py @@ -0,0 +1,802 @@ +"""SCons.Tool + +SCons tool selection. + +This looks for modules that define a callable object that can modify +a construction environment as appropriate for a given tool (or tool +chain). + +Note that because this subsystem just *selects* a callable that can +modify a construction environment, it's possible for people to define +their own "tool specification" in an arbitrary callable function. No +one needs to use or tie in to this subsystem in order to roll their own +tool definition. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Tool/__init__.py 2013/03/03 09:48:35 garyo" + +import imp +import sys +import re +import os +import shutil + +import SCons.Builder +import SCons.Errors +import SCons.Node.FS +import SCons.Scanner +import SCons.Scanner.C +import SCons.Scanner.D +import SCons.Scanner.LaTeX +import SCons.Scanner.Prog + +DefaultToolpath=[] + +CScanner = SCons.Scanner.C.CScanner() +DScanner = SCons.Scanner.D.DScanner() +LaTeXScanner = SCons.Scanner.LaTeX.LaTeXScanner() +PDFLaTeXScanner = SCons.Scanner.LaTeX.PDFLaTeXScanner() +ProgramScanner = SCons.Scanner.Prog.ProgramScanner() +SourceFileScanner = SCons.Scanner.Base({}, name='SourceFileScanner') + +CSuffixes = [".c", ".C", ".cxx", ".cpp", ".c++", ".cc", + ".h", ".H", ".hxx", ".hpp", ".hh", + ".F", ".fpp", ".FPP", + ".m", ".mm", + ".S", ".spp", ".SPP", ".sx"] + +DSuffixes = ['.d'] + +IDLSuffixes = [".idl", ".IDL"] + +LaTeXSuffixes = [".tex", ".ltx", ".latex"] + +for suffix in CSuffixes: + SourceFileScanner.add_scanner(suffix, CScanner) + +for suffix in DSuffixes: + SourceFileScanner.add_scanner(suffix, DScanner) + +# FIXME: what should be done here? Two scanners scan the same extensions, +# but look for different files, e.g., "picture.eps" vs. "picture.pdf". +# The builders for DVI and PDF explicitly reference their scanners +# I think that means this is not needed??? +for suffix in LaTeXSuffixes: + SourceFileScanner.add_scanner(suffix, LaTeXScanner) + SourceFileScanner.add_scanner(suffix, PDFLaTeXScanner) + +class Tool(object): + def __init__(self, name, toolpath=[], **kw): + self.name = name + self.toolpath = toolpath + DefaultToolpath + # remember these so we can merge them into the call + self.init_kw = kw + + module = self._tool_module() + self.generate = module.generate + self.exists = module.exists + if hasattr(module, 'options'): + self.options = module.options + + def _tool_module(self): + # TODO: Interchange zipimport with normal initilization for better error reporting + oldpythonpath = sys.path + sys.path = self.toolpath + sys.path + + try: + try: + file, path, desc = imp.find_module(self.name, self.toolpath) + try: + return imp.load_module(self.name, file, path, desc) + finally: + if file: + file.close() + except ImportError, e: + if str(e)!="No module named %s"%self.name: + raise SCons.Errors.EnvironmentError(e) + try: + import zipimport + except ImportError: + pass + else: + for aPath in self.toolpath: + try: + importer = zipimport.zipimporter(aPath) + return importer.load_module(self.name) + except ImportError, e: + pass + finally: + sys.path = oldpythonpath + + full_name = 'SCons.Tool.' + self.name + try: + return sys.modules[full_name] + except KeyError: + try: + smpath = sys.modules['SCons.Tool'].__path__ + try: + file, path, desc = imp.find_module(self.name, smpath) + module = imp.load_module(full_name, file, path, desc) + setattr(SCons.Tool, self.name, module) + if file: + file.close() + return module + except ImportError, e: + if str(e)!="No module named %s"%self.name: + raise SCons.Errors.EnvironmentError(e) + try: + import zipimport + importer = zipimport.zipimporter( sys.modules['SCons.Tool'].__path__[0] ) + module = importer.load_module(full_name) + setattr(SCons.Tool, self.name, module) + return module + except ImportError, e: + m = "No tool named '%s': %s" % (self.name, e) + raise SCons.Errors.EnvironmentError(m) + except ImportError, e: + m = "No tool named '%s': %s" % (self.name, e) + raise SCons.Errors.EnvironmentError(m) + + def __call__(self, env, *args, **kw): + if self.init_kw is not None: + # Merge call kws into init kws; + # but don't bash self.init_kw. + if kw is not None: + call_kw = kw + kw = self.init_kw.copy() + kw.update(call_kw) + else: + kw = self.init_kw + env.Append(TOOLS = [ self.name ]) + if hasattr(self, 'options'): + import SCons.Variables + if 'options' not in env: + from SCons.Script import ARGUMENTS + env['options']=SCons.Variables.Variables(args=ARGUMENTS) + opts=env['options'] + + self.options(opts) + opts.Update(env) + + self.generate(env, *args, **kw) + + def __str__(self): + return self.name + +########################################################################## +# Create common executable program / library / object builders + +def createProgBuilder(env): + """This is a utility function that creates the Program + Builder in an Environment if it is not there already. + + If it is already there, we return the existing one. + """ + + try: + program = env['BUILDERS']['Program'] + except KeyError: + import SCons.Defaults + program = SCons.Builder.Builder(action = SCons.Defaults.LinkAction, + emitter = '$PROGEMITTER', + prefix = '$PROGPREFIX', + suffix = '$PROGSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'Object', + target_scanner = ProgramScanner) + env['BUILDERS']['Program'] = program + + return program + +def createStaticLibBuilder(env): + """This is a utility function that creates the StaticLibrary + Builder in an Environment if it is not there already. + + If it is already there, we return the existing one. + """ + + try: + static_lib = env['BUILDERS']['StaticLibrary'] + except KeyError: + action_list = [ SCons.Action.Action("$ARCOM", "$ARCOMSTR") ] + if env.Detect('ranlib'): + ranlib_action = SCons.Action.Action("$RANLIBCOM", "$RANLIBCOMSTR") + action_list.append(ranlib_action) + + static_lib = SCons.Builder.Builder(action = action_list, + emitter = '$LIBEMITTER', + prefix = '$LIBPREFIX', + suffix = '$LIBSUFFIX', + src_suffix = '$OBJSUFFIX', + src_builder = 'StaticObject') + env['BUILDERS']['StaticLibrary'] = static_lib + env['BUILDERS']['Library'] = static_lib + + return static_lib + +def VersionShLibLinkNames(version, libname, env): + """Generate names of symlinks to the versioned shared library""" + Verbose = False + platform = env.subst('$PLATFORM') + shlib_suffix = env.subst('$SHLIBSUFFIX') + shlink_flags = SCons.Util.CLVar(env.subst('$SHLINKFLAGS')) + + linknames = [] + if version.count(".") != 2: + # We need a version string of the form x.y.z to proceed + # Several changes need to be made to support versions like x.y + raise ValueError + + if platform == 'darwin': + # For libfoo.x.y.z.dylib, linknames libfoo.so + suffix_re = re.escape('.' + version + shlib_suffix) + linkname = re.sub(suffix_re, shlib_suffix, libname) + if Verbose: + print "VersionShLibLinkNames: linkname = ",linkname + linknames.append(linkname) + elif platform == 'posix': + # For libfoo.so.x.y.z, linknames libfoo.so libfoo.so.x.y libfoo.so.x + suffix_re = re.escape(shlib_suffix + '.' + version) + # First linkname has no version number + linkname = re.sub(suffix_re, shlib_suffix, libname) + if Verbose: + print "VersionShLibLinkNames: linkname = ",linkname + linknames.append(linkname) + versionparts = version.split('.') + major_name = linkname + "." + versionparts[0] + minor_name = major_name + "." + versionparts[1] + #Only add link for major_name + #for linkname in [major_name, minor_name]: + for linkname in [major_name, ]: + if Verbose: + print "VersionShLibLinkNames: linkname ",linkname, ", target ",libname + linknames.append(linkname) + # note: no Windows case here (win32 or cygwin); + # MSVC doesn't support this type of versioned shared libs. + # (could probably do something for MinGW though) + return linknames + +def VersionedSharedLibrary(target = None, source= None, env=None): + """Build a shared library. If the environment has SHLIBVERSION +defined make a versioned shared library and create the appropriate +symlinks for the platform we are on""" + Verbose = False + try: + version = env.subst('$SHLIBVERSION') + except KeyError: + version = None + + # libname includes the version number if one was given + libname = target[0].name + platform = env.subst('$PLATFORM') + shlib_suffix = env.subst('$SHLIBSUFFIX') + shlink_flags = SCons.Util.CLVar(env.subst('$SHLINKFLAGS')) + if Verbose: + print "VersionShLib: libname = ",libname + print "VersionShLib: platform = ",platform + print "VersionShLib: shlib_suffix = ",shlib_suffix + print "VersionShLib: target = ",str(target[0]) + + if version: + # set the shared library link flags + if platform == 'posix': + suffix_re = re.escape(shlib_suffix + '.' + version) + (major, age, revision) = version.split(".") + # soname will have only the major version number in it + soname = re.sub(suffix_re, shlib_suffix, libname) + '.' + major + shlink_flags += [ '-Wl,-Bsymbolic', '-Wl,-soname=%s' % soname ] + if Verbose: + print " soname ",soname,", shlink_flags ",shlink_flags + elif platform == 'cygwin': + shlink_flags += [ '-Wl,-Bsymbolic', + '-Wl,--out-implib,${TARGET.base}.a' ] + elif platform == 'darwin': + shlink_flags += [ '-current_version', '%s' % version, + '-compatibility_version', '%s' % version, + '-undefined', 'dynamic_lookup' ] + if Verbose: + print "VersionShLib: shlink_flags = ",shlink_flags + envlink = env.Clone() + envlink['SHLINKFLAGS'] = shlink_flags + else: + envlink = env + + result = SCons.Defaults.ShLinkAction(target, source, envlink) + + if version: + # here we need the full pathname so the links end up in the right directory + libname = target[0].path + linknames = VersionShLibLinkNames(version, libname, env) + if Verbose: + print "VerShLib: linknames ",linknames + # Here we just need the file name w/o path as the target of the link + lib_ver = target[0].name + # make symlink of adjacent names in linknames + for count in range(len(linknames)): + linkname = linknames[count] + if count > 0: + os.symlink(os.path.basename(linkname),lastname) + if Verbose: + print "VerShLib: made sym link of %s -> %s" % (lastname,linkname) + lastname = linkname + # finish chain of sym links with link to the actual library + if len(linknames)>0: + os.symlink(lib_ver,lastname) + if Verbose: + print "VerShLib: made sym link of %s -> %s" % (lib_ver,linkname) + return result + +ShLibAction = SCons.Action.Action(VersionedSharedLibrary, None) + +def createSharedLibBuilder(env): + """This is a utility function that creates the SharedLibrary + Builder in an Environment if it is not there already. + + If it is already there, we return the existing one. + """ + + try: + shared_lib = env['BUILDERS']['SharedLibrary'] + except KeyError: + import SCons.Defaults + action_list = [ SCons.Defaults.SharedCheck, + ShLibAction ] + shared_lib = SCons.Builder.Builder(action = action_list, + emitter = "$SHLIBEMITTER", + prefix = '$SHLIBPREFIX', + suffix = '$SHLIBSUFFIX', + target_scanner = ProgramScanner, + src_suffix = '$SHOBJSUFFIX', + src_builder = 'SharedObject') + env['BUILDERS']['SharedLibrary'] = shared_lib + + return shared_lib + +def createLoadableModuleBuilder(env): + """This is a utility function that creates the LoadableModule + Builder in an Environment if it is not there already. + + If it is already there, we return the existing one. + """ + + try: + ld_module = env['BUILDERS']['LoadableModule'] + except KeyError: + import SCons.Defaults + action_list = [ SCons.Defaults.SharedCheck, + SCons.Defaults.LdModuleLinkAction ] + ld_module = SCons.Builder.Builder(action = action_list, + emitter = "$LDMODULEEMITTER", + prefix = '$LDMODULEPREFIX', + suffix = '$LDMODULESUFFIX', + target_scanner = ProgramScanner, + src_suffix = '$SHOBJSUFFIX', + src_builder = 'SharedObject') + env['BUILDERS']['LoadableModule'] = ld_module + + return ld_module + +def createObjBuilders(env): + """This is a utility function that creates the StaticObject + and SharedObject Builders in an Environment if they + are not there already. + + If they are there already, we return the existing ones. + + This is a separate function because soooo many Tools + use this functionality. + + The return is a 2-tuple of (StaticObject, SharedObject) + """ + + + try: + static_obj = env['BUILDERS']['StaticObject'] + except KeyError: + static_obj = SCons.Builder.Builder(action = {}, + emitter = {}, + prefix = '$OBJPREFIX', + suffix = '$OBJSUFFIX', + src_builder = ['CFile', 'CXXFile'], + source_scanner = SourceFileScanner, + single_source = 1) + env['BUILDERS']['StaticObject'] = static_obj + env['BUILDERS']['Object'] = static_obj + + try: + shared_obj = env['BUILDERS']['SharedObject'] + except KeyError: + shared_obj = SCons.Builder.Builder(action = {}, + emitter = {}, + prefix = '$SHOBJPREFIX', + suffix = '$SHOBJSUFFIX', + src_builder = ['CFile', 'CXXFile'], + source_scanner = SourceFileScanner, + single_source = 1) + env['BUILDERS']['SharedObject'] = shared_obj + + return (static_obj, shared_obj) + +def createCFileBuilders(env): + """This is a utility function that creates the CFile/CXXFile + Builders in an Environment if they + are not there already. + + If they are there already, we return the existing ones. + + This is a separate function because soooo many Tools + use this functionality. + + The return is a 2-tuple of (CFile, CXXFile) + """ + + try: + c_file = env['BUILDERS']['CFile'] + except KeyError: + c_file = SCons.Builder.Builder(action = {}, + emitter = {}, + suffix = {None:'$CFILESUFFIX'}) + env['BUILDERS']['CFile'] = c_file + + env.SetDefault(CFILESUFFIX = '.c') + + try: + cxx_file = env['BUILDERS']['CXXFile'] + except KeyError: + cxx_file = SCons.Builder.Builder(action = {}, + emitter = {}, + suffix = {None:'$CXXFILESUFFIX'}) + env['BUILDERS']['CXXFile'] = cxx_file + env.SetDefault(CXXFILESUFFIX = '.cc') + + return (c_file, cxx_file) + +########################################################################## +# Create common Java builders + +def CreateJarBuilder(env): + try: + java_jar = env['BUILDERS']['Jar'] + except KeyError: + fs = SCons.Node.FS.get_default_fs() + jar_com = SCons.Action.Action('$JARCOM', '$JARCOMSTR') + java_jar = SCons.Builder.Builder(action = jar_com, + suffix = '$JARSUFFIX', + src_suffix = '$JAVACLASSSUFIX', + src_builder = 'JavaClassFile', + source_factory = fs.Entry) + env['BUILDERS']['Jar'] = java_jar + return java_jar + +def CreateJavaHBuilder(env): + try: + java_javah = env['BUILDERS']['JavaH'] + except KeyError: + fs = SCons.Node.FS.get_default_fs() + java_javah_com = SCons.Action.Action('$JAVAHCOM', '$JAVAHCOMSTR') + java_javah = SCons.Builder.Builder(action = java_javah_com, + src_suffix = '$JAVACLASSSUFFIX', + target_factory = fs.Entry, + source_factory = fs.File, + src_builder = 'JavaClassFile') + env['BUILDERS']['JavaH'] = java_javah + return java_javah + +def CreateJavaClassFileBuilder(env): + try: + java_class_file = env['BUILDERS']['JavaClassFile'] + except KeyError: + fs = SCons.Node.FS.get_default_fs() + javac_com = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR') + java_class_file = SCons.Builder.Builder(action = javac_com, + emitter = {}, + #suffix = '$JAVACLASSSUFFIX', + src_suffix = '$JAVASUFFIX', + src_builder = ['JavaFile'], + target_factory = fs.Entry, + source_factory = fs.File) + env['BUILDERS']['JavaClassFile'] = java_class_file + return java_class_file + +def CreateJavaClassDirBuilder(env): + try: + java_class_dir = env['BUILDERS']['JavaClassDir'] + except KeyError: + fs = SCons.Node.FS.get_default_fs() + javac_com = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR') + java_class_dir = SCons.Builder.Builder(action = javac_com, + emitter = {}, + target_factory = fs.Dir, + source_factory = fs.Dir) + env['BUILDERS']['JavaClassDir'] = java_class_dir + return java_class_dir + +def CreateJavaFileBuilder(env): + try: + java_file = env['BUILDERS']['JavaFile'] + except KeyError: + java_file = SCons.Builder.Builder(action = {}, + emitter = {}, + suffix = {None:'$JAVASUFFIX'}) + env['BUILDERS']['JavaFile'] = java_file + env['JAVASUFFIX'] = '.java' + return java_file + +class ToolInitializerMethod(object): + """ + This is added to a construction environment in place of a + method(s) normally called for a Builder (env.Object, env.StaticObject, + etc.). When called, it has its associated ToolInitializer + object search the specified list of tools and apply the first + one that exists to the construction environment. It then calls + whatever builder was (presumably) added to the construction + environment in place of this particular instance. + """ + def __init__(self, name, initializer): + """ + Note: we store the tool name as __name__ so it can be used by + the class that attaches this to a construction environment. + """ + self.__name__ = name + self.initializer = initializer + + def get_builder(self, env): + """ + Returns the appropriate real Builder for this method name + after having the associated ToolInitializer object apply + the appropriate Tool module. + """ + builder = getattr(env, self.__name__) + + self.initializer.apply_tools(env) + + builder = getattr(env, self.__name__) + if builder is self: + # There was no Builder added, which means no valid Tool + # for this name was found (or possibly there's a mismatch + # between the name we were called by and the Builder name + # added by the Tool module). + return None + + self.initializer.remove_methods(env) + + return builder + + def __call__(self, env, *args, **kw): + """ + """ + builder = self.get_builder(env) + if builder is None: + return [], [] + return builder(*args, **kw) + +class ToolInitializer(object): + """ + A class for delayed initialization of Tools modules. + + Instances of this class associate a list of Tool modules with + a list of Builder method names that will be added by those Tool + modules. As part of instantiating this object for a particular + construction environment, we also add the appropriate + ToolInitializerMethod objects for the various Builder methods + that we want to use to delay Tool searches until necessary. + """ + def __init__(self, env, tools, names): + if not SCons.Util.is_List(tools): + tools = [tools] + if not SCons.Util.is_List(names): + names = [names] + self.env = env + self.tools = tools + self.names = names + self.methods = {} + for name in names: + method = ToolInitializerMethod(name, self) + self.methods[name] = method + env.AddMethod(method) + + def remove_methods(self, env): + """ + Removes the methods that were added by the tool initialization + so we no longer copy and re-bind them when the construction + environment gets cloned. + """ + for method in self.methods.values(): + env.RemoveMethod(method) + + def apply_tools(self, env): + """ + Searches the list of associated Tool modules for one that + exists, and applies that to the construction environment. + """ + for t in self.tools: + tool = SCons.Tool.Tool(t) + if tool.exists(env): + env.Tool(tool) + return + + # If we fall through here, there was no tool module found. + # This is where we can put an informative error message + # about the inability to find the tool. We'll start doing + # this as we cut over more pre-defined Builder+Tools to use + # the ToolInitializer class. + +def Initializers(env): + ToolInitializer(env, ['install'], ['_InternalInstall', '_InternalInstallAs', '_InternalInstallVersionedLib']) + def Install(self, *args, **kw): + return self._InternalInstall(*args, **kw) + def InstallAs(self, *args, **kw): + return self._InternalInstallAs(*args, **kw) + def InstallVersionedLib(self, *args, **kw): + return self._InternalInstallVersionedLib(*args, **kw) + env.AddMethod(Install) + env.AddMethod(InstallAs) + env.AddMethod(InstallVersionedLib) + +def FindTool(tools, env): + for tool in tools: + t = Tool(tool) + if t.exists(env): + return tool + return None + +def FindAllTools(tools, env): + def ToolExists(tool, env=env): + return Tool(tool).exists(env) + return list(filter (ToolExists, tools)) + +def tool_list(platform, env): + + other_plat_tools=[] + # XXX this logic about what tool to prefer on which platform + # should be moved into either the platform files or + # the tool files themselves. + # The search orders here are described in the man page. If you + # change these search orders, update the man page as well. + if str(platform) == 'win32': + "prefer Microsoft tools on Windows" + linkers = ['mslink', 'gnulink', 'ilink', 'linkloc', 'ilink32' ] + c_compilers = ['msvc', 'mingw', 'gcc', 'intelc', 'icl', 'icc', 'cc', 'bcc32' ] + cxx_compilers = ['msvc', 'intelc', 'icc', 'g++', 'c++', 'bcc32' ] + assemblers = ['masm', 'nasm', 'gas', '386asm' ] + fortran_compilers = ['gfortran', 'g77', 'ifl', 'cvf', 'f95', 'f90', 'fortran'] + ars = ['mslib', 'ar', 'tlib'] + other_plat_tools=['msvs','midl'] + elif str(platform) == 'os2': + "prefer IBM tools on OS/2" + linkers = ['ilink', 'gnulink', ]#'mslink'] + c_compilers = ['icc', 'gcc',]# 'msvc', 'cc'] + cxx_compilers = ['icc', 'g++',]# 'msvc', 'c++'] + assemblers = ['nasm',]# 'masm', 'gas'] + fortran_compilers = ['ifl', 'g77'] + ars = ['ar',]# 'mslib'] + elif str(platform) == 'irix': + "prefer MIPSPro on IRIX" + linkers = ['sgilink', 'gnulink'] + c_compilers = ['sgicc', 'gcc', 'cc'] + cxx_compilers = ['sgic++', 'g++', 'c++'] + assemblers = ['as', 'gas'] + fortran_compilers = ['f95', 'f90', 'f77', 'g77', 'fortran'] + ars = ['sgiar'] + elif str(platform) == 'sunos': + "prefer Forte tools on SunOS" + linkers = ['sunlink', 'gnulink'] + c_compilers = ['suncc', 'gcc', 'cc'] + cxx_compilers = ['sunc++', 'g++', 'c++'] + assemblers = ['as', 'gas'] + fortran_compilers = ['sunf95', 'sunf90', 'sunf77', 'f95', 'f90', 'f77', + 'gfortran', 'g77', 'fortran'] + ars = ['sunar'] + elif str(platform) == 'hpux': + "prefer aCC tools on HP-UX" + linkers = ['hplink', 'gnulink'] + c_compilers = ['hpcc', 'gcc', 'cc'] + cxx_compilers = ['hpc++', 'g++', 'c++'] + assemblers = ['as', 'gas'] + fortran_compilers = ['f95', 'f90', 'f77', 'g77', 'fortran'] + ars = ['ar'] + elif str(platform) == 'aix': + "prefer AIX Visual Age tools on AIX" + linkers = ['aixlink', 'gnulink'] + c_compilers = ['aixcc', 'gcc', 'cc'] + cxx_compilers = ['aixc++', 'g++', 'c++'] + assemblers = ['as', 'gas'] + fortran_compilers = ['f95', 'f90', 'aixf77', 'g77', 'fortran'] + ars = ['ar'] + elif str(platform) == 'darwin': + "prefer GNU tools on Mac OS X, except for some linkers and IBM tools" + linkers = ['applelink', 'gnulink'] + c_compilers = ['gcc', 'cc'] + cxx_compilers = ['g++', 'c++'] + assemblers = ['as'] + fortran_compilers = ['gfortran', 'f95', 'f90', 'g77'] + ars = ['ar'] + else: + "prefer GNU tools on all other platforms" + linkers = ['gnulink', 'mslink', 'ilink'] + c_compilers = ['gcc', 'msvc', 'intelc', 'icc', 'cc'] + cxx_compilers = ['g++', 'msvc', 'intelc', 'icc', 'c++'] + assemblers = ['gas', 'nasm', 'masm'] + fortran_compilers = ['gfortran', 'g77', 'ifort', 'ifl', 'f95', 'f90', 'f77'] + ars = ['ar', 'mslib'] + + c_compiler = FindTool(c_compilers, env) or c_compilers[0] + + # XXX this logic about what tool provides what should somehow be + # moved into the tool files themselves. + if c_compiler and c_compiler == 'mingw': + # MinGW contains a linker, C compiler, C++ compiler, + # Fortran compiler, archiver and assembler: + cxx_compiler = None + linker = None + assembler = None + fortran_compiler = None + ar = None + else: + # Don't use g++ if the C compiler has built-in C++ support: + if c_compiler in ('msvc', 'intelc', 'icc'): + cxx_compiler = None + else: + cxx_compiler = FindTool(cxx_compilers, env) or cxx_compilers[0] + linker = FindTool(linkers, env) or linkers[0] + assembler = FindTool(assemblers, env) or assemblers[0] + fortran_compiler = FindTool(fortran_compilers, env) or fortran_compilers[0] + ar = FindTool(ars, env) or ars[0] + + other_tools = FindAllTools(other_plat_tools + [ + 'dmd', + #TODO: merge 'install' into 'filesystem' and + # make 'filesystem' the default + 'filesystem', + 'm4', + 'wix', #'midl', 'msvs', + # Parser generators + 'lex', 'yacc', + # Foreign function interface + 'rpcgen', 'swig', + # Java + 'jar', 'javac', 'javah', 'rmic', + # TeX + 'dvipdf', 'dvips', 'gs', + 'tex', 'latex', 'pdflatex', 'pdftex', + # Archivers + 'tar', 'zip', 'rpm', + # SourceCode factories + 'BitKeeper', 'CVS', 'Perforce', + 'RCS', 'SCCS', # 'Subversion', + ], env) + + tools = ([linker, c_compiler, cxx_compiler, + fortran_compiler, assembler, ar] + + other_tools) + + return [x for x in tools if x] + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: + diff --git a/scons/scons-local-2.3.0/SCons/Tool/aixc++.py b/scons/scons-local-2.3.0/SCons/Tool/aixc++.py new file mode 100644 index 000000000..e9cc5c232 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/aixc++.py @@ -0,0 +1,82 @@ +"""SCons.Tool.aixc++ + +Tool-specific initialization for IBM xlC / Visual Age C++ compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/aixc++.py 2013/03/03 09:48:35 garyo" + +import os.path + +import SCons.Platform.aix + +cplusplus = __import__('c++', globals(), locals(), []) + +packages = ['vacpp.cmp.core', 'vacpp.cmp.batch', 'vacpp.cmp.C', 'ibmcxx.cmp'] + +def get_xlc(env): + xlc = env.get('CXX', 'xlC') + xlc_r = env.get('SHCXX', 'xlC_r') + return SCons.Platform.aix.get_xlc(env, xlc, xlc_r, packages) + +def smart_cxxflags(source, target, env, for_signature): + build_dir = env.GetBuildPath() + if build_dir: + return '-qtempinc=' + os.path.join(build_dir, 'tempinc') + return '' + +def generate(env): + """Add Builders and construction variables for xlC / Visual Age + suite to an Environment.""" + path, _cxx, _shcxx, version = get_xlc(env) + if path: + _cxx = os.path.join(path, _cxx) + _shcxx = os.path.join(path, _shcxx) + + cplusplus.generate(env) + + env['CXX'] = _cxx + env['SHCXX'] = _shcxx + env['CXXVERSION'] = version + env['SHOBJSUFFIX'] = '.pic.o' + +def exists(env): + path, _cxx, _shcxx, version = get_xlc(env) + if path and _cxx: + xlc = os.path.join(path, _cxx) + if os.path.exists(xlc): + return xlc + return None + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/aixcc.py b/scons/scons-local-2.3.0/SCons/Tool/aixcc.py new file mode 100644 index 000000000..44cb0ab14 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/aixcc.py @@ -0,0 +1,74 @@ +"""SCons.Tool.aixcc + +Tool-specific initialization for IBM xlc / Visual Age C compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/aixcc.py 2013/03/03 09:48:35 garyo" + +import os.path + +import SCons.Platform.aix + +import cc + +packages = ['vac.C', 'ibmcxx.cmp'] + +def get_xlc(env): + xlc = env.get('CC', 'xlc') + xlc_r = env.get('SHCC', 'xlc_r') + return SCons.Platform.aix.get_xlc(env, xlc, xlc_r, packages) + +def generate(env): + """Add Builders and construction variables for xlc / Visual Age + suite to an Environment.""" + path, _cc, _shcc, version = get_xlc(env) + if path: + _cc = os.path.join(path, _cc) + _shcc = os.path.join(path, _shcc) + + cc.generate(env) + + env['CC'] = _cc + env['SHCC'] = _shcc + env['CCVERSION'] = version + +def exists(env): + path, _cc, _shcc, version = get_xlc(env) + if path and _cc: + xlc = os.path.join(path, _cc) + if os.path.exists(xlc): + return xlc + return None + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/aixf77.py b/scons/scons-local-2.3.0/SCons/Tool/aixf77.py new file mode 100644 index 000000000..19f8594b4 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/aixf77.py @@ -0,0 +1,80 @@ +"""engine.SCons.Tool.aixf77 + +Tool-specific initialization for IBM Visual Age f77 Fortran compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/aixf77.py 2013/03/03 09:48:35 garyo" + +import os.path + +#import SCons.Platform.aix + +import f77 + +# It would be good to look for the AIX F77 package the same way we're now +# looking for the C and C++ packages. This should be as easy as supplying +# the correct package names in the following list and uncommenting the +# SCons.Platform.aix_get_xlc() call the in the function below. +packages = [] + +def get_xlf77(env): + xlf77 = env.get('F77', 'xlf77') + xlf77_r = env.get('SHF77', 'xlf77_r') + #return SCons.Platform.aix.get_xlc(env, xlf77, xlf77_r, packages) + return (None, xlf77, xlf77_r, None) + +def generate(env): + """ + Add Builders and construction variables for the Visual Age FORTRAN + compiler to an Environment. + """ + path, _f77, _shf77, version = get_xlf77(env) + if path: + _f77 = os.path.join(path, _f77) + _shf77 = os.path.join(path, _shf77) + + f77.generate(env) + + env['F77'] = _f77 + env['SHF77'] = _shf77 + +def exists(env): + path, _f77, _shf77, version = get_xlf77(env) + if path and _f77: + xlf77 = os.path.join(path, _f77) + if os.path.exists(xlf77): + return xlf77 + return None + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/aixlink.py b/scons/scons-local-2.3.0/SCons/Tool/aixlink.py new file mode 100644 index 000000000..980568a3a --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/aixlink.py @@ -0,0 +1,76 @@ +"""SCons.Tool.aixlink + +Tool-specific initialization for the IBM Visual Age linker. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/aixlink.py 2013/03/03 09:48:35 garyo" + +import os +import os.path + +import SCons.Util + +import aixcc +import link + +cplusplus = __import__('c++', globals(), locals(), []) + +def smart_linkflags(source, target, env, for_signature): + if cplusplus.iscplusplus(source): + build_dir = env.subst('$BUILDDIR', target=target, source=source) + if build_dir: + return '-qtempinc=' + os.path.join(build_dir, 'tempinc') + return '' + +def generate(env): + """ + Add Builders and construction variables for Visual Age linker to + an Environment. + """ + link.generate(env) + + env['SMARTLINKFLAGS'] = smart_linkflags + env['LINKFLAGS'] = SCons.Util.CLVar('$SMARTLINKFLAGS') + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -qmkshrobj -qsuppress=1501-218') + env['SHLIBSUFFIX'] = '.a' + +def exists(env): + path, _cc, _shcc, version = aixcc.get_xlc(env) + if path and _cc: + xlc = os.path.join(path, _cc) + if os.path.exists(xlc): + return xlc + return None + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/applelink.py b/scons/scons-local-2.3.0/SCons/Tool/applelink.py new file mode 100644 index 000000000..a67fc10cc --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/applelink.py @@ -0,0 +1,71 @@ +"""SCons.Tool.applelink + +Tool-specific initialization for the Apple gnu-like linker. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/applelink.py 2013/03/03 09:48:35 garyo" + +import SCons.Util + +# Even though the Mac is based on the GNU toolchain, it doesn't understand +# the -rpath option, so we use the "link" tool instead of "gnulink". +import link + +def generate(env): + """Add Builders and construction variables for applelink to an + Environment.""" + link.generate(env) + + env['FRAMEWORKPATHPREFIX'] = '-F' + env['_FRAMEWORKPATH'] = '${_concat(FRAMEWORKPATHPREFIX, FRAMEWORKPATH, "", __env__)}' + env['_FRAMEWORKS'] = '${_concat("-framework ", FRAMEWORKS, "", __env__)}' + env['LINKCOM'] = env['LINKCOM'] + ' $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -dynamiclib') + env['SHLINKCOM'] = env['SHLINKCOM'] + ' $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' + + # override the default for loadable modules, which are different + # on OS X than dynamic shared libs. echoing what XCode does for + # pre/suffixes: + env['LDMODULEPREFIX'] = '' + env['LDMODULESUFFIX'] = '' + env['LDMODULEFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -bundle') + env['LDMODULECOM'] = '$LDMODULE -o ${TARGET} $LDMODULEFLAGS $SOURCES $_LIBDIRFLAGS $_LIBFLAGS $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' + + + +def exists(env): + return env['PLATFORM'] == 'darwin' + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/ar.py b/scons/scons-local-2.3.0/SCons/Tool/ar.py new file mode 100644 index 000000000..c11b5437d --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/ar.py @@ -0,0 +1,63 @@ +"""SCons.Tool.ar + +Tool-specific initialization for ar (library archive). + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/ar.py 2013/03/03 09:48:35 garyo" + +import SCons.Defaults +import SCons.Tool +import SCons.Util + + +def generate(env): + """Add Builders and construction variables for ar to an Environment.""" + SCons.Tool.createStaticLibBuilder(env) + + env['AR'] = 'ar' + env['ARFLAGS'] = SCons.Util.CLVar('rc') + env['ARCOM'] = '$AR $ARFLAGS $TARGET $SOURCES' + env['LIBPREFIX'] = 'lib' + env['LIBSUFFIX'] = '.a' + + if env.Detect('ranlib'): + env['RANLIB'] = 'ranlib' + env['RANLIBFLAGS'] = SCons.Util.CLVar('') + env['RANLIBCOM'] = '$RANLIB $RANLIBFLAGS $TARGET' + +def exists(env): + return env.Detect('ar') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/as.py b/scons/scons-local-2.3.0/SCons/Tool/as.py new file mode 100644 index 000000000..29b0266e7 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/as.py @@ -0,0 +1,78 @@ +"""SCons.Tool.as + +Tool-specific initialization for as, the generic Posix assembler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/as.py 2013/03/03 09:48:35 garyo" + +import SCons.Defaults +import SCons.Tool +import SCons.Util + +assemblers = ['as'] + +ASSuffixes = ['.s', '.asm', '.ASM'] +ASPPSuffixes = ['.spp', '.SPP', '.sx'] +if SCons.Util.case_sensitive_suffixes('.s', '.S'): + ASPPSuffixes.extend(['.S']) +else: + ASSuffixes.extend(['.S']) + +def generate(env): + """Add Builders and construction variables for as to an Environment.""" + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + for suffix in ASSuffixes: + static_obj.add_action(suffix, SCons.Defaults.ASAction) + shared_obj.add_action(suffix, SCons.Defaults.ASAction) + static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) + shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) + + for suffix in ASPPSuffixes: + static_obj.add_action(suffix, SCons.Defaults.ASPPAction) + shared_obj.add_action(suffix, SCons.Defaults.ASPPAction) + static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) + shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) + + env['AS'] = env.Detect(assemblers) or 'as' + env['ASFLAGS'] = SCons.Util.CLVar('') + env['ASCOM'] = '$AS $ASFLAGS -o $TARGET $SOURCES' + env['ASPPFLAGS'] = '$ASFLAGS' + env['ASPPCOM'] = '$CC $ASPPFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES' + +def exists(env): + return env.Detect(assemblers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/bcc32.py b/scons/scons-local-2.3.0/SCons/Tool/bcc32.py new file mode 100644 index 000000000..c152353b4 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/bcc32.py @@ -0,0 +1,81 @@ +"""SCons.Tool.bcc32 + +XXX + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/bcc32.py 2013/03/03 09:48:35 garyo" + +import os +import os.path + +import SCons.Defaults +import SCons.Tool +import SCons.Util + +def findIt(program, env): + # First search in the SCons path and then the OS path: + borwin = env.WhereIs(program) or SCons.Util.WhereIs(program) + if borwin: + dir = os.path.dirname(borwin) + env.PrependENVPath('PATH', dir) + return borwin + +def generate(env): + findIt('bcc32', env) + """Add Builders and construction variables for bcc to an + Environment.""" + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + for suffix in ['.c', '.cpp']: + static_obj.add_action(suffix, SCons.Defaults.CAction) + shared_obj.add_action(suffix, SCons.Defaults.ShCAction) + static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) + shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) + + env['CC'] = 'bcc32' + env['CCFLAGS'] = SCons.Util.CLVar('') + env['CFLAGS'] = SCons.Util.CLVar('') + env['CCCOM'] = '$CC -q $CFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o$TARGET $SOURCES' + env['SHCC'] = '$CC' + env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') + env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS') + env['SHCCCOM'] = '$SHCC -WD $SHCFLAGS $SHCCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o$TARGET $SOURCES' + env['CPPDEFPREFIX'] = '-D' + env['CPPDEFSUFFIX'] = '' + env['INCPREFIX'] = '-I' + env['INCSUFFIX'] = '' + env['SHOBJSUFFIX'] = '.dll' + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 0 + env['CFILESUFFIX'] = '.cpp' + +def exists(env): + return findIt('bcc32', env) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/c++.py b/scons/scons-local-2.3.0/SCons/Tool/c++.py new file mode 100644 index 000000000..18f4afe29 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/c++.py @@ -0,0 +1,99 @@ +"""SCons.Tool.c++ + +Tool-specific initialization for generic Posix C++ compilers. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/c++.py 2013/03/03 09:48:35 garyo" + +import os.path + +import SCons.Tool +import SCons.Defaults +import SCons.Util + +compilers = ['CC', 'c++'] + +CXXSuffixes = ['.cpp', '.cc', '.cxx', '.c++', '.C++', '.mm'] +if SCons.Util.case_sensitive_suffixes('.c', '.C'): + CXXSuffixes.append('.C') + +def iscplusplus(source): + if not source: + # Source might be None for unusual cases like SConf. + return 0 + for s in source: + if s.sources: + ext = os.path.splitext(str(s.sources[0]))[1] + if ext in CXXSuffixes: + return 1 + return 0 + +def generate(env): + """ + Add Builders and construction variables for Visual Age C++ compilers + to an Environment. + """ + import SCons.Tool + import SCons.Tool.cc + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + for suffix in CXXSuffixes: + static_obj.add_action(suffix, SCons.Defaults.CXXAction) + shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction) + static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) + shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) + + SCons.Tool.cc.add_common_cc_variables(env) + + env['CXX'] = 'c++' + env['CXXFLAGS'] = SCons.Util.CLVar('') + env['CXXCOM'] = '$CXX -o $TARGET -c $CXXFLAGS $CCFLAGS $_CCCOMCOM $SOURCES' + env['SHCXX'] = '$CXX' + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') + env['SHCXXCOM'] = '$SHCXX -o $TARGET -c $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM $SOURCES' + + env['CPPDEFPREFIX'] = '-D' + env['CPPDEFSUFFIX'] = '' + env['INCPREFIX'] = '-I' + env['INCSUFFIX'] = '' + env['SHOBJSUFFIX'] = '.os' + env['OBJSUFFIX'] = '.o' + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 0 + + env['CXXFILESUFFIX'] = '.cc' + +def exists(env): + return env.Detect(compilers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/cc.py b/scons/scons-local-2.3.0/SCons/Tool/cc.py new file mode 100644 index 000000000..00ecc2f25 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/cc.py @@ -0,0 +1,102 @@ +"""SCons.Tool.cc + +Tool-specific initialization for generic Posix C compilers. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/cc.py 2013/03/03 09:48:35 garyo" + +import SCons.Tool +import SCons.Defaults +import SCons.Util + +CSuffixes = ['.c', '.m'] +if not SCons.Util.case_sensitive_suffixes('.c', '.C'): + CSuffixes.append('.C') + +def add_common_cc_variables(env): + """ + Add underlying common "C compiler" variables that + are used by multiple tools (specifically, c++). + """ + if '_CCCOMCOM' not in env: + env['_CCCOMCOM'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS' + # It's a hack to test for darwin here, but the alternative + # of creating an applecc.py to contain this seems overkill. + # Maybe someday the Apple platform will require more setup and + # this logic will be moved. + env['FRAMEWORKS'] = SCons.Util.CLVar('') + env['FRAMEWORKPATH'] = SCons.Util.CLVar('') + if env['PLATFORM'] == 'darwin': + env['_CCCOMCOM'] = env['_CCCOMCOM'] + ' $_FRAMEWORKPATH' + + if 'CCFLAGS' not in env: + env['CCFLAGS'] = SCons.Util.CLVar('') + + if 'SHCCFLAGS' not in env: + env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') + +def generate(env): + """ + Add Builders and construction variables for C compilers to an Environment. + """ + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + for suffix in CSuffixes: + static_obj.add_action(suffix, SCons.Defaults.CAction) + shared_obj.add_action(suffix, SCons.Defaults.ShCAction) + static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) + shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) + + add_common_cc_variables(env) + + env['CC'] = 'cc' + env['CFLAGS'] = SCons.Util.CLVar('') + env['CCCOM'] = '$CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCCOMCOM $SOURCES' + env['SHCC'] = '$CC' + env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS') + env['SHCCCOM'] = '$SHCC -o $TARGET -c $SHCFLAGS $SHCCFLAGS $_CCCOMCOM $SOURCES' + + env['CPPDEFPREFIX'] = '-D' + env['CPPDEFSUFFIX'] = '' + env['INCPREFIX'] = '-I' + env['INCSUFFIX'] = '' + env['SHOBJSUFFIX'] = '.os' + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 0 + + env['CFILESUFFIX'] = '.c' + +def exists(env): + return env.Detect('cc') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/cvf.py b/scons/scons-local-2.3.0/SCons/Tool/cvf.py new file mode 100644 index 000000000..2bf10da79 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/cvf.py @@ -0,0 +1,58 @@ +"""engine.SCons.Tool.cvf + +Tool-specific initialization for the Compaq Visual Fortran compiler. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/cvf.py 2013/03/03 09:48:35 garyo" + +import fortran + +compilers = ['f90'] + +def generate(env): + """Add Builders and construction variables for compaq visual fortran to an Environment.""" + + fortran.generate(env) + + env['FORTRAN'] = 'f90' + env['FORTRANCOM'] = '$FORTRAN $FORTRANFLAGS $_FORTRANMODFLAG $_FORTRANINCFLAGS /compile_only ${SOURCES.windows} /object:${TARGET.windows}' + env['FORTRANPPCOM'] = '$FORTRAN $FORTRANFLAGS $CPPFLAGS $_CPPDEFFLAGS $_FORTRANMODFLAG $_FORTRANINCFLAGS /compile_only ${SOURCES.windows} /object:${TARGET.windows}' + env['SHFORTRANCOM'] = '$SHFORTRAN $SHFORTRANFLAGS $_FORTRANMODFLAG $_FORTRANINCFLAGS /compile_only ${SOURCES.windows} /object:${TARGET.windows}' + env['SHFORTRANPPCOM'] = '$SHFORTRAN $SHFORTRANFLAGS $CPPFLAGS $_CPPDEFFLAGS $_FORTRANMODFLAG $_FORTRANINCFLAGS /compile_only ${SOURCES.windows} /object:${TARGET.windows}' + env['OBJSUFFIX'] = '.obj' + env['FORTRANMODDIR'] = '${TARGET.dir}' + env['FORTRANMODDIRPREFIX'] = '/module:' + env['FORTRANMODDIRSUFFIX'] = '' + +def exists(env): + return env.Detect(compilers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/default.py b/scons/scons-local-2.3.0/SCons/Tool/default.py new file mode 100644 index 000000000..4faab7ee1 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/default.py @@ -0,0 +1,50 @@ +"""SCons.Tool.default + +Initialization with a default tool list. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/default.py 2013/03/03 09:48:35 garyo" + +import SCons.Tool + +def generate(env): + """Add default tools.""" + for t in SCons.Tool.tool_list(env['PLATFORM'], env): + SCons.Tool.Tool(t)(env) + +def exists(env): + return 1 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/dmd.py b/scons/scons-local-2.3.0/SCons/Tool/dmd.py new file mode 100644 index 000000000..3c73f80ea --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/dmd.py @@ -0,0 +1,240 @@ +"""SCons.Tool.dmd + +Tool-specific initialization for the Digital Mars D compiler. +(http://digitalmars.com/d) + +Coded by Andy Friesen (andy@ikagames.com) +15 November 2003 + +Amended by Russel Winder (russel@russel.org.uk) +2010-02-07 + +There are a number of problems with this script at this point in time. +The one that irritates me the most is the Windows linker setup. The D +linker doesn't have a way to add lib paths on the commandline, as far +as I can see. You have to specify paths relative to the SConscript or +use absolute paths. To hack around it, add '#/blah'. This will link +blah.lib from the directory where SConstruct resides. + +Compiler variables: + DC - The name of the D compiler to use. Defaults to dmd or gdmd, + whichever is found. + DPATH - List of paths to search for import modules. + DVERSIONS - List of version tags to enable when compiling. + DDEBUG - List of debug tags to enable when compiling. + +Linker related variables: + LIBS - List of library files to link in. + DLINK - Name of the linker to use. Defaults to dmd or gdmd. + DLINKFLAGS - List of linker flags. + +Lib tool variables: + DLIB - Name of the lib tool to use. Defaults to lib. + DLIBFLAGS - List of flags to pass to the lib tool. + LIBS - Same as for the linker. (libraries to pull into the .lib) +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/dmd.py 2013/03/03 09:48:35 garyo" + +import os + +import SCons.Action +import SCons.Builder +import SCons.Defaults +import SCons.Scanner.D +import SCons.Tool + +# Adapted from c++.py +def isD(source): + if not source: + return 0 + + for s in source: + if s.sources: + ext = os.path.splitext(str(s.sources[0]))[1] + if ext == '.d': + return 1 + return 0 + +smart_link = {} + +smart_lib = {} + +def generate(env): + global smart_link + global smart_lib + + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + DAction = SCons.Action.Action('$DCOM', '$DCOMSTR') + + static_obj.add_action('.d', DAction) + shared_obj.add_action('.d', DAction) + static_obj.add_emitter('.d', SCons.Defaults.StaticObjectEmitter) + shared_obj.add_emitter('.d', SCons.Defaults.SharedObjectEmitter) + + dc = env.Detect(['dmd', 'gdmd']) + env['DC'] = dc + env['DCOM'] = '$DC $_DINCFLAGS $_DVERFLAGS $_DDEBUGFLAGS $_DFLAGS -c -of$TARGET $SOURCES' + env['_DINCFLAGS'] = '$( ${_concat(DINCPREFIX, DPATH, DINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' + env['_DVERFLAGS'] = '$( ${_concat(DVERPREFIX, DVERSIONS, DVERSUFFIX, __env__)} $)' + env['_DDEBUGFLAGS'] = '$( ${_concat(DDEBUGPREFIX, DDEBUG, DDEBUGSUFFIX, __env__)} $)' + env['_DFLAGS'] = '$( ${_concat(DFLAGPREFIX, DFLAGS, DFLAGSUFFIX, __env__)} $)' + + env['DPATH'] = ['#/'] + env['DFLAGS'] = [] + env['DVERSIONS'] = [] + env['DDEBUG'] = [] + + if dc: + # Add the path to the standard library. + # This is merely for the convenience of the dependency scanner. + dmd_path = env.WhereIs(dc) + if dmd_path: + x = dmd_path.rindex(dc) + phobosDir = dmd_path[:x] + '/../src/phobos' + if os.path.isdir(phobosDir): + env.Append(DPATH = [phobosDir]) + + env['DINCPREFIX'] = '-I' + env['DINCSUFFIX'] = '' + env['DVERPREFIX'] = '-version=' + env['DVERSUFFIX'] = '' + env['DDEBUGPREFIX'] = '-debug=' + env['DDEBUGSUFFIX'] = '' + env['DFLAGPREFIX'] = '-' + env['DFLAGSUFFIX'] = '' + env['DFILESUFFIX'] = '.d' + + # Need to use the Digital Mars linker/lib on windows. + # *nix can just use GNU link. + if env['PLATFORM'] == 'win32': + env['DLINK'] = '$DC' + env['DLINKCOM'] = '$DLINK -of$TARGET $SOURCES $DFLAGS $DLINKFLAGS $_DLINKLIBFLAGS' + env['DLIB'] = 'lib' + env['DLIBCOM'] = '$DLIB $_DLIBFLAGS -c $TARGET $SOURCES $_DLINKLIBFLAGS' + + env['_DLINKLIBFLAGS'] = '$( ${_concat(DLIBLINKPREFIX, LIBS, DLIBLINKSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' + env['_DLIBFLAGS'] = '$( ${_concat(DLIBFLAGPREFIX, DLIBFLAGS, DLIBFLAGSUFFIX, __env__)} $)' + env['DLINKFLAGS'] = [] + env['DLIBLINKPREFIX'] = '' + env['DLIBLINKSUFFIX'] = '.lib' + env['DLIBFLAGPREFIX'] = '-' + env['DLIBFLAGSUFFIX'] = '' + env['DLINKFLAGPREFIX'] = '-' + env['DLINKFLAGSUFFIX'] = '' + + SCons.Tool.createStaticLibBuilder(env) + + # Basically, we hijack the link and ar builders with our own. + # these builders check for the presence of D source, and swap out + # the system's defaults for the Digital Mars tools. If there's no D + # source, then we silently return the previous settings. + linkcom = env.get('LINKCOM') + try: + env['SMART_LINKCOM'] = smart_link[linkcom] + except KeyError: + def _smartLink(source, target, env, for_signature, + defaultLinker=linkcom): + if isD(source): + # XXX I'm not sure how to add a $DLINKCOMSTR variable + # so that it works with this _smartLink() logic, + # and I don't have a D compiler/linker to try it out, + # so we'll leave it alone for now. + return '$DLINKCOM' + else: + return defaultLinker + env['SMART_LINKCOM'] = smart_link[linkcom] = _smartLink + + arcom = env.get('ARCOM') + try: + env['SMART_ARCOM'] = smart_lib[arcom] + except KeyError: + def _smartLib(source, target, env, for_signature, + defaultLib=arcom): + if isD(source): + # XXX I'm not sure how to add a $DLIBCOMSTR variable + # so that it works with this _smartLib() logic, and + # I don't have a D compiler/archiver to try it out, + # so we'll leave it alone for now. + return '$DLIBCOM' + else: + return defaultLib + env['SMART_ARCOM'] = smart_lib[arcom] = _smartLib + + # It is worth noting that the final space in these strings is + # absolutely pivotal. SCons sees these as actions and not generators + # if it is not there. (very bad) + env['ARCOM'] = '$SMART_ARCOM ' + env['LINKCOM'] = '$SMART_LINKCOM ' + else: # assuming linux + linkcom = env.get('LINKCOM') + try: + env['SMART_LINKCOM'] = smart_link[linkcom] + except KeyError: + def _smartLink(source, target, env, for_signature, + defaultLinker=linkcom, dc=dc): + if isD(source): + try: + libs = env['LIBS'] + except KeyError: + libs = [] + if dc == 'dmd': + # TODO: This assumes that the dmd executable is in the + # bin directory and that the libraries are in a peer + # directory lib. This true of the Digital Mars + # distribution but . . . + import glob + dHome = env.WhereIs(dc).replace('/dmd' , '/..') + if glob.glob(dHome + '/lib/*phobos2*'): + if 'phobos2' not in libs: + env.Append(LIBPATH = [dHome + '/lib']) + env.Append(LIBS = ['phobos2']) + # TODO: Find out when there will be a + # 64-bit version of D. + env.Append(LINKFLAGS = ['-m32']) + else: + if 'phobos' not in libs: + env.Append(LIBS = ['phobos']) + elif dc is 'gdmd': + env.Append(LIBS = ['gphobos']) + if 'pthread' not in libs: + env.Append(LIBS = ['pthread']) + if 'm' not in libs: + env.Append(LIBS = ['m']) + return defaultLinker + env['SMART_LINKCOM'] = smart_link[linkcom] = _smartLink + + env['LINKCOM'] = '$SMART_LINKCOM ' + +def exists(env): + return env.Detect(['dmd', 'gdmd']) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/dvi.py b/scons/scons-local-2.3.0/SCons/Tool/dvi.py new file mode 100644 index 000000000..496e6988f --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/dvi.py @@ -0,0 +1,64 @@ +"""SCons.Tool.dvi + +Common DVI Builder definition for various other Tool modules that use it. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/dvi.py 2013/03/03 09:48:35 garyo" + +import SCons.Builder +import SCons.Tool + +DVIBuilder = None + +def generate(env): + try: + env['BUILDERS']['DVI'] + except KeyError: + global DVIBuilder + + if DVIBuilder is None: + # The suffix is hard-coded to '.dvi', not configurable via a + # construction variable like $DVISUFFIX, because the output + # file name is hard-coded within TeX. + DVIBuilder = SCons.Builder.Builder(action = {}, + source_scanner = SCons.Tool.LaTeXScanner, + suffix = '.dvi', + emitter = {}, + source_ext_match = None) + + env['BUILDERS']['DVI'] = DVIBuilder + +def exists(env): + # This only puts a skeleton Builder in place, so if someone + # references this Tool directly, it's always "available." + return 1 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/dvipdf.py b/scons/scons-local-2.3.0/SCons/Tool/dvipdf.py new file mode 100644 index 000000000..5fe8d8892 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/dvipdf.py @@ -0,0 +1,125 @@ +"""SCons.Tool.dvipdf + +Tool-specific initialization for dvipdf. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Tool/dvipdf.py 2013/03/03 09:48:35 garyo" + +import SCons.Action +import SCons.Defaults +import SCons.Tool.pdf +import SCons.Tool.tex +import SCons.Util + +_null = SCons.Scanner.LaTeX._null + +def DviPdfPsFunction(XXXDviAction, target = None, source= None, env=None): + """A builder for DVI files that sets the TEXPICTS environment + variable before running dvi2ps or dvipdf.""" + + try: + abspath = source[0].attributes.path + except AttributeError : + abspath = '' + + saved_env = SCons.Scanner.LaTeX.modify_env_var(env, 'TEXPICTS', abspath) + + result = XXXDviAction(target, source, env) + + if saved_env is _null: + try: + del env['ENV']['TEXPICTS'] + except KeyError: + pass # was never set + else: + env['ENV']['TEXPICTS'] = saved_env + + return result + +def DviPdfFunction(target = None, source= None, env=None): + result = DviPdfPsFunction(PDFAction,target,source,env) + return result + +def DviPdfStrFunction(target = None, source= None, env=None): + """A strfunction for dvipdf that returns the appropriate + command string for the no_exec options.""" + if env.GetOption("no_exec"): + result = env.subst('$DVIPDFCOM',0,target,source) + else: + result = '' + return result + +PDFAction = None +DVIPDFAction = None + +def PDFEmitter(target, source, env): + """Strips any .aux or .log files from the input source list. + These are created by the TeX Builder that in all likelihood was + used to generate the .dvi file we're using as input, and we only + care about the .dvi file. + """ + def strip_suffixes(n): + return not SCons.Util.splitext(str(n))[1] in ['.aux', '.log'] + source = list(filter(strip_suffixes, source)) + return (target, source) + +def generate(env): + """Add Builders and construction variables for dvipdf to an Environment.""" + global PDFAction + if PDFAction is None: + PDFAction = SCons.Action.Action('$DVIPDFCOM', '$DVIPDFCOMSTR') + + global DVIPDFAction + if DVIPDFAction is None: + DVIPDFAction = SCons.Action.Action(DviPdfFunction, strfunction = DviPdfStrFunction) + + import pdf + pdf.generate(env) + + bld = env['BUILDERS']['PDF'] + bld.add_action('.dvi', DVIPDFAction) + bld.add_emitter('.dvi', PDFEmitter) + + env['DVIPDF'] = 'dvipdf' + env['DVIPDFFLAGS'] = SCons.Util.CLVar('') + env['DVIPDFCOM'] = 'cd ${TARGET.dir} && $DVIPDF $DVIPDFFLAGS ${SOURCE.file} ${TARGET.file}' + + # Deprecated synonym. + env['PDFCOM'] = ['$DVIPDFCOM'] + +def exists(env): + SCons.Tool.tex.generate_darwin(env) + return env.Detect('dvipdf') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/dvips.py b/scons/scons-local-2.3.0/SCons/Tool/dvips.py new file mode 100644 index 000000000..a0f9e6fa8 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/dvips.py @@ -0,0 +1,95 @@ +"""SCons.Tool.dvips + +Tool-specific initialization for dvips. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/dvips.py 2013/03/03 09:48:35 garyo" + +import SCons.Action +import SCons.Builder +import SCons.Tool.dvipdf +import SCons.Util + +def DviPsFunction(target = None, source= None, env=None): + result = SCons.Tool.dvipdf.DviPdfPsFunction(PSAction,target,source,env) + return result + +def DviPsStrFunction(target = None, source= None, env=None): + """A strfunction for dvipdf that returns the appropriate + command string for the no_exec options.""" + if env.GetOption("no_exec"): + result = env.subst('$PSCOM',0,target,source) + else: + result = '' + return result + +PSAction = None +DVIPSAction = None +PSBuilder = None + +def generate(env): + """Add Builders and construction variables for dvips to an Environment.""" + global PSAction + if PSAction is None: + PSAction = SCons.Action.Action('$PSCOM', '$PSCOMSTR') + + global DVIPSAction + if DVIPSAction is None: + DVIPSAction = SCons.Action.Action(DviPsFunction, strfunction = DviPsStrFunction) + + global PSBuilder + if PSBuilder is None: + PSBuilder = SCons.Builder.Builder(action = PSAction, + prefix = '$PSPREFIX', + suffix = '$PSSUFFIX', + src_suffix = '.dvi', + src_builder = 'DVI', + single_source=True) + + env['BUILDERS']['PostScript'] = PSBuilder + + env['DVIPS'] = 'dvips' + env['DVIPSFLAGS'] = SCons.Util.CLVar('') + # I'm not quite sure I got the directories and filenames right for variant_dir + # We need to be in the correct directory for the sake of latex \includegraphics eps included files. + env['PSCOM'] = 'cd ${TARGET.dir} && $DVIPS $DVIPSFLAGS -o ${TARGET.file} ${SOURCE.file}' + env['PSPREFIX'] = '' + env['PSSUFFIX'] = '.ps' + +def exists(env): + SCons.Tool.tex.generate_darwin(env) + return env.Detect('dvips') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/f03.py b/scons/scons-local-2.3.0/SCons/Tool/f03.py new file mode 100644 index 000000000..791562541 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/f03.py @@ -0,0 +1,63 @@ +"""engine.SCons.Tool.f03 + +Tool-specific initialization for the generic Posix f03 Fortran compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/f03.py 2013/03/03 09:48:35 garyo" + +import SCons.Defaults +import SCons.Tool +import SCons.Util +import fortran +from SCons.Tool.FortranCommon import add_all_to_env, add_f03_to_env + +compilers = ['f03'] + +def generate(env): + add_all_to_env(env) + add_f03_to_env(env) + + fcomp = env.Detect(compilers) or 'f03' + env['F03'] = fcomp + env['SHF03'] = fcomp + + env['FORTRAN'] = fcomp + env['SHFORTRAN'] = fcomp + + +def exists(env): + return env.Detect(compilers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/f77.py b/scons/scons-local-2.3.0/SCons/Tool/f77.py new file mode 100644 index 000000000..7f49892bb --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/f77.py @@ -0,0 +1,62 @@ +"""engine.SCons.Tool.f77 + +Tool-specific initialization for the generic Posix f77 Fortran compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/f77.py 2013/03/03 09:48:35 garyo" + +import SCons.Defaults +import SCons.Scanner.Fortran +import SCons.Tool +import SCons.Util +from SCons.Tool.FortranCommon import add_all_to_env, add_f77_to_env + +compilers = ['f77'] + +def generate(env): + add_all_to_env(env) + add_f77_to_env(env) + + fcomp = env.Detect(compilers) or 'f77' + env['F77'] = fcomp + env['SHF77'] = fcomp + + env['FORTRAN'] = fcomp + env['SHFORTRAN'] = fcomp + +def exists(env): + return env.Detect(compilers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/f90.py b/scons/scons-local-2.3.0/SCons/Tool/f90.py new file mode 100644 index 000000000..bd07ae0a5 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/f90.py @@ -0,0 +1,62 @@ +"""engine.SCons.Tool.f90 + +Tool-specific initialization for the generic Posix f90 Fortran compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/f90.py 2013/03/03 09:48:35 garyo" + +import SCons.Defaults +import SCons.Scanner.Fortran +import SCons.Tool +import SCons.Util +from SCons.Tool.FortranCommon import add_all_to_env, add_f90_to_env + +compilers = ['f90'] + +def generate(env): + add_all_to_env(env) + add_f90_to_env(env) + + fc = env.Detect(compilers) or 'f90' + env['F90'] = fc + env['SHF90'] = fc + + env['FORTRAN'] = fc + env['SHFORTRAN'] = fc + +def exists(env): + return env.Detect(compilers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/f95.py b/scons/scons-local-2.3.0/SCons/Tool/f95.py new file mode 100644 index 000000000..5c16d484c --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/f95.py @@ -0,0 +1,63 @@ +"""engine.SCons.Tool.f95 + +Tool-specific initialization for the generic Posix f95 Fortran compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/f95.py 2013/03/03 09:48:35 garyo" + +import SCons.Defaults +import SCons.Tool +import SCons.Util +import fortran +from SCons.Tool.FortranCommon import add_all_to_env, add_f95_to_env + +compilers = ['f95'] + +def generate(env): + add_all_to_env(env) + add_f95_to_env(env) + + fcomp = env.Detect(compilers) or 'f95' + env['F95'] = fcomp + env['SHF95'] = fcomp + + env['FORTRAN'] = fcomp + env['SHFORTRAN'] = fcomp + + +def exists(env): + return env.Detect(compilers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/filesystem.py b/scons/scons-local-2.3.0/SCons/Tool/filesystem.py new file mode 100644 index 000000000..431ad4c2b --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/filesystem.py @@ -0,0 +1,98 @@ +"""SCons.Tool.filesystem + +Tool-specific initialization for the filesystem tools. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/filesystem.py 2013/03/03 09:48:35 garyo" + +import SCons +from SCons.Tool.install import copyFunc + +copyToBuilder, copyAsBuilder = None, None + +def copyto_emitter(target, source, env): + """ changes the path of the source to be under the target (which + are assumed to be directories. + """ + n_target = [] + + for t in target: + n_target = n_target + [t.File( str( s ) ) for s in source] + + return (n_target, source) + +def copy_action_func(target, source, env): + assert( len(target) == len(source) ), "\ntarget: %s\nsource: %s" %(list(map(str, target)),list(map(str, source))) + + for t, s in zip(target, source): + if copyFunc(t.get_path(), s.get_path(), env): + return 1 + + return 0 + +def copy_action_str(target, source, env): + return env.subst_target_source(env['COPYSTR'], 0, target, source) + +copy_action = SCons.Action.Action( copy_action_func, copy_action_str ) + +def generate(env): + try: + env['BUILDERS']['CopyTo'] + env['BUILDERS']['CopyAs'] + except KeyError, e: + global copyToBuilder + if copyToBuilder is None: + copyToBuilder = SCons.Builder.Builder( + action = copy_action, + target_factory = env.fs.Dir, + source_factory = env.fs.Entry, + multi = 1, + emitter = [ copyto_emitter, ] ) + + global copyAsBuilder + if copyAsBuilder is None: + copyAsBuilder = SCons.Builder.Builder( + action = copy_action, + target_factory = env.fs.Entry, + source_factory = env.fs.Entry ) + + env['BUILDERS']['CopyTo'] = copyToBuilder + env['BUILDERS']['CopyAs'] = copyAsBuilder + + env['COPYSTR'] = 'Copy file(s): "$SOURCES" to "$TARGETS"' + +def exists(env): + return 1 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/fortran.py b/scons/scons-local-2.3.0/SCons/Tool/fortran.py new file mode 100644 index 000000000..35fcd5360 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/fortran.py @@ -0,0 +1,62 @@ +"""SCons.Tool.fortran + +Tool-specific initialization for a generic Posix f77/f90 Fortran compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/fortran.py 2013/03/03 09:48:35 garyo" + +import re + +import SCons.Action +import SCons.Defaults +import SCons.Scanner.Fortran +import SCons.Tool +import SCons.Util +from SCons.Tool.FortranCommon import add_all_to_env, add_fortran_to_env + +compilers = ['f95', 'f90', 'f77'] + +def generate(env): + add_all_to_env(env) + add_fortran_to_env(env) + + fc = env.Detect(compilers) or 'f77' + env['SHFORTRAN'] = fc + env['FORTRAN'] = fc + +def exists(env): + return env.Detect(compilers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/g++.py b/scons/scons-local-2.3.0/SCons/Tool/g++.py new file mode 100644 index 000000000..be7d93536 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/g++.py @@ -0,0 +1,90 @@ +"""SCons.Tool.g++ + +Tool-specific initialization for g++. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/g++.py 2013/03/03 09:48:35 garyo" + +import os.path +import re +import subprocess + +import SCons.Tool +import SCons.Util + +cplusplus = __import__('c++', globals(), locals(), []) + +compilers = ['g++'] + +def generate(env): + """Add Builders and construction variables for g++ to an Environment.""" + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + cplusplus.generate(env) + + env['CXX'] = env.Detect(compilers) + + # platform specific settings + if env['PLATFORM'] == 'aix': + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -mminimal-toc') + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 + env['SHOBJSUFFIX'] = '$OBJSUFFIX' + elif env['PLATFORM'] == 'hpux': + env['SHOBJSUFFIX'] = '.pic.o' + elif env['PLATFORM'] == 'sunos': + env['SHOBJSUFFIX'] = '.pic.o' + # determine compiler version + if env['CXX']: + #pipe = SCons.Action._subproc(env, [env['CXX'], '-dumpversion'], + pipe = SCons.Action._subproc(env, [env['CXX'], '--version'], + stdin = 'devnull', + stderr = 'devnull', + stdout = subprocess.PIPE) + if pipe.wait() != 0: return + # -dumpversion was added in GCC 3.0. As long as we're supporting + # GCC versions older than that, we should use --version and a + # regular expression. + #line = pipe.stdout.read().strip() + #if line: + # env['CXXVERSION'] = line + line = pipe.stdout.readline() + match = re.search(r'[0-9]+(\.[0-9]+)+', line) + if match: + env['CXXVERSION'] = match.group(0) + +def exists(env): + return env.Detect(compilers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/g77.py b/scons/scons-local-2.3.0/SCons/Tool/g77.py new file mode 100644 index 000000000..5e1d1a1e6 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/g77.py @@ -0,0 +1,73 @@ +"""engine.SCons.Tool.g77 + +Tool-specific initialization for g77. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/g77.py 2013/03/03 09:48:35 garyo" + +import SCons.Util +from SCons.Tool.FortranCommon import add_all_to_env, add_f77_to_env + +compilers = ['g77', 'f77'] + +def generate(env): + """Add Builders and construction variables for g77 to an Environment.""" + add_all_to_env(env) + add_f77_to_env(env) + + fcomp = env.Detect(compilers) or 'g77' + if env['PLATFORM'] in ['cygwin', 'win32']: + env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS') + env['SHF77FLAGS'] = SCons.Util.CLVar('$F77FLAGS') + else: + env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -fPIC') + env['SHF77FLAGS'] = SCons.Util.CLVar('$F77FLAGS -fPIC') + + env['FORTRAN'] = fcomp + env['SHFORTRAN'] = '$FORTRAN' + + env['F77'] = fcomp + env['SHF77'] = '$F77' + + env['INCFORTRANPREFIX'] = "-I" + env['INCFORTRANSUFFIX'] = "" + + env['INCF77PREFIX'] = "-I" + env['INCF77SUFFIX'] = "" + +def exists(env): + return env.Detect(compilers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/gas.py b/scons/scons-local-2.3.0/SCons/Tool/gas.py new file mode 100644 index 000000000..143ede936 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/gas.py @@ -0,0 +1,53 @@ +"""SCons.Tool.gas + +Tool-specific initialization for as, the Gnu assembler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/gas.py 2013/03/03 09:48:35 garyo" + +as_module = __import__('as', globals(), locals(), []) + +assemblers = ['as', 'gas'] + +def generate(env): + """Add Builders and construction variables for as to an Environment.""" + as_module.generate(env) + + env['AS'] = env.Detect(assemblers) or 'as' + +def exists(env): + return env.Detect(assemblers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/gcc.py b/scons/scons-local-2.3.0/SCons/Tool/gcc.py new file mode 100644 index 000000000..5b49ff83f --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/gcc.py @@ -0,0 +1,80 @@ +"""SCons.Tool.gcc + +Tool-specific initialization for gcc. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/gcc.py 2013/03/03 09:48:35 garyo" + +import cc +import os +import re +import subprocess + +import SCons.Util + +compilers = ['gcc', 'cc'] + +def generate(env): + """Add Builders and construction variables for gcc to an Environment.""" + cc.generate(env) + + env['CC'] = env.Detect(compilers) or 'gcc' + if env['PLATFORM'] in ['cygwin', 'win32']: + env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') + else: + env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS -fPIC') + # determine compiler version + if env['CC']: + #pipe = SCons.Action._subproc(env, [env['CC'], '-dumpversion'], + pipe = SCons.Action._subproc(env, [env['CC'], '--version'], + stdin = 'devnull', + stderr = 'devnull', + stdout = subprocess.PIPE) + if pipe.wait() != 0: return + # -dumpversion was added in GCC 3.0. As long as we're supporting + # GCC versions older than that, we should use --version and a + # regular expression. + #line = pipe.stdout.read().strip() + #if line: + # env['CCVERSION'] = line + line = pipe.stdout.readline() + match = re.search(r'[0-9]+(\.[0-9]+)+', line) + if match: + env['CCVERSION'] = match.group(0) + +def exists(env): + return env.Detect(compilers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/gettext.py b/scons/scons-local-2.3.0/SCons/Tool/gettext.py new file mode 100644 index 000000000..b561eda40 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/gettext.py @@ -0,0 +1,48 @@ +"""gettext tool +""" + + +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Tool/gettext.py 2013/03/03 09:48:35 garyo" + +############################################################################# +def generate(env,**kw): + import SCons.Tool + from SCons.Tool.GettextCommon \ + import _translate, tool_list + for t in tool_list(env['PLATFORM'], env): + env.Tool(t) + env.AddMethod(_translate, 'Translate') +############################################################################# + +############################################################################# +def exists(env): + from SCons.Tool.GettextCommon \ + import _xgettext_exists, _msginit_exists, \ + _msgmerge_exists, _msgfmt_exists + try: + return _xgettext_exists(env) and _msginit_exists(env) \ + and _msgmerge_exists(env) and _msgfmt_exists(env) + except: + return False +############################################################################# diff --git a/scons/scons-local-2.3.0/SCons/Tool/gfortran.py b/scons/scons-local-2.3.0/SCons/Tool/gfortran.py new file mode 100644 index 000000000..463f70e78 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/gfortran.py @@ -0,0 +1,64 @@ +"""SCons.Tool.gfortran + +Tool-specific initialization for gfortran, the GNU Fortran 95/Fortran +2003 compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/gfortran.py 2013/03/03 09:48:35 garyo" + +import SCons.Util + +import fortran + +def generate(env): + """Add Builders and construction variables for gfortran to an + Environment.""" + fortran.generate(env) + + for dialect in ['F77', 'F90', 'FORTRAN', 'F95', 'F03']: + env['%s' % dialect] = 'gfortran' + env['SH%s' % dialect] = '$%s' % dialect + if env['PLATFORM'] in ['cygwin', 'win32']: + env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS' % dialect) + else: + env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS -fPIC' % dialect) + + env['INC%sPREFIX' % dialect] = "-I" + env['INC%sSUFFIX' % dialect] = "" + +def exists(env): + return env.Detect('gfortran') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/gnulink.py b/scons/scons-local-2.3.0/SCons/Tool/gnulink.py new file mode 100644 index 000000000..281a0d082 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/gnulink.py @@ -0,0 +1,62 @@ +"""SCons.Tool.gnulink + +Tool-specific initialization for the gnu linker. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/gnulink.py 2013/03/03 09:48:35 garyo" + +import SCons.Util + +import link + +linkers = ['g++', 'gcc'] + +def generate(env): + """Add Builders and construction variables for gnulink to an Environment.""" + link.generate(env) + + if env['PLATFORM'] == 'hpux': + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared -fPIC') + + # __RPATH is set to $_RPATH in the platform specification if that + # platform supports it. + env['RPATHPREFIX'] = '-Wl,-rpath=' + env['RPATHSUFFIX'] = '' + env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}' + +def exists(env): + return env.Detect(linkers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/gs.py b/scons/scons-local-2.3.0/SCons/Tool/gs.py new file mode 100644 index 000000000..fc53b7e0a --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/gs.py @@ -0,0 +1,81 @@ +"""SCons.Tool.gs + +Tool-specific initialization for Ghostscript. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/gs.py 2013/03/03 09:48:35 garyo" + +import SCons.Action +import SCons.Platform +import SCons.Util + +# Ghostscript goes by different names on different platforms... +platform = SCons.Platform.platform_default() + +if platform == 'os2': + gs = 'gsos2' +elif platform == 'win32': + gs = 'gswin32c' +else: + gs = 'gs' + +GhostscriptAction = None + +def generate(env): + """Add Builders and construction variables for Ghostscript to an + Environment.""" + + global GhostscriptAction + if GhostscriptAction is None: + GhostscriptAction = SCons.Action.Action('$GSCOM', '$GSCOMSTR') + + import pdf + pdf.generate(env) + + bld = env['BUILDERS']['PDF'] + bld.add_action('.ps', GhostscriptAction) + + env['GS'] = gs + env['GSFLAGS'] = SCons.Util.CLVar('-dNOPAUSE -dBATCH -sDEVICE=pdfwrite') + env['GSCOM'] = '$GS $GSFLAGS -sOutputFile=$TARGET $SOURCES' + + +def exists(env): + if 'PS2PDF' in env: + return env.Detect(env['PS2PDF']) + else: + return env.Detect(gs) or SCons.Util.WhereIs(gs) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/hpc++.py b/scons/scons-local-2.3.0/SCons/Tool/hpc++.py new file mode 100644 index 000000000..94b7fec7a --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/hpc++.py @@ -0,0 +1,84 @@ +"""SCons.Tool.hpc++ + +Tool-specific initialization for c++ on HP/UX. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/hpc++.py 2013/03/03 09:48:35 garyo" + +import os.path + +import SCons.Util + +cplusplus = __import__('c++', globals(), locals(), []) + +acc = None + +# search for the acc compiler and linker front end + +try: + dirs = os.listdir('/opt') +except (IOError, OSError): + # Not being able to read the directory because it doesn't exist + # (IOError) or isn't readable (OSError) is okay. + dirs = [] + +for dir in dirs: + cc = '/opt/' + dir + '/bin/aCC' + if os.path.exists(cc): + acc = cc + break + + +def generate(env): + """Add Builders and construction variables for g++ to an Environment.""" + cplusplus.generate(env) + + if acc: + env['CXX'] = acc or 'aCC' + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS +Z') + # determine version of aCC + line = os.popen(acc + ' -V 2>&1').readline().rstrip() + if line.find('aCC: HP ANSI C++') == 0: + env['CXXVERSION'] = line.split()[-1] + + if env['PLATFORM'] == 'cygwin': + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') + else: + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS +Z') + +def exists(env): + return acc + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/hpcc.py b/scons/scons-local-2.3.0/SCons/Tool/hpcc.py new file mode 100644 index 000000000..efd523026 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/hpcc.py @@ -0,0 +1,53 @@ +"""SCons.Tool.hpcc + +Tool-specific initialization for HP aCC and cc. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/hpcc.py 2013/03/03 09:48:35 garyo" + +import SCons.Util + +import cc + +def generate(env): + """Add Builders and construction variables for aCC & cc to an Environment.""" + cc.generate(env) + + env['CXX'] = 'aCC' + env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS +Z') + +def exists(env): + return env.Detect('aCC') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/hplink.py b/scons/scons-local-2.3.0/SCons/Tool/hplink.py new file mode 100644 index 000000000..627c9e946 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/hplink.py @@ -0,0 +1,77 @@ +"""SCons.Tool.hplink + +Tool-specific initialization for the HP linker. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/hplink.py 2013/03/03 09:48:35 garyo" + +import os +import os.path + +import SCons.Util + +import link + +ccLinker = None + +# search for the acc compiler and linker front end + +try: + dirs = os.listdir('/opt') +except (IOError, OSError): + # Not being able to read the directory because it doesn't exist + # (IOError) or isn't readable (OSError) is okay. + dirs = [] + +for dir in dirs: + linker = '/opt/' + dir + '/bin/aCC' + if os.path.exists(linker): + ccLinker = linker + break + +def generate(env): + """ + Add Builders and construction variables for Visual Age linker to + an Environment. + """ + link.generate(env) + + env['LINKFLAGS'] = SCons.Util.CLVar('-Wl,+s -Wl,+vnocompatwarnings') + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -b') + env['SHLIBSUFFIX'] = '.sl' + +def exists(env): + return ccLinker + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/icc.py b/scons/scons-local-2.3.0/SCons/Tool/icc.py new file mode 100644 index 000000000..aa54a0ae2 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/icc.py @@ -0,0 +1,59 @@ +"""engine.SCons.Tool.icc + +Tool-specific initialization for the OS/2 icc compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/icc.py 2013/03/03 09:48:35 garyo" + +import cc + +def generate(env): + """Add Builders and construction variables for the OS/2 to an Environment.""" + cc.generate(env) + + env['CC'] = 'icc' + env['CCCOM'] = '$CC $CFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET' + env['CXXCOM'] = '$CXX $CXXFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET' + env['CPPDEFPREFIX'] = '/D' + env['CPPDEFSUFFIX'] = '' + env['INCPREFIX'] = '/I' + env['INCSUFFIX'] = '' + env['CFILESUFFIX'] = '.c' + env['CXXFILESUFFIX'] = '.cc' + +def exists(env): + return env.Detect('icc') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/icl.py b/scons/scons-local-2.3.0/SCons/Tool/icl.py new file mode 100644 index 000000000..f0a252ba0 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/icl.py @@ -0,0 +1,52 @@ +"""engine.SCons.Tool.icl + +Tool-specific initialization for the Intel C/C++ compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/icl.py 2013/03/03 09:48:35 garyo" + +import SCons.Tool.intelc + +# This has been completely superceded by intelc.py, which can +# handle both Windows and Linux versions. + +def generate(*args, **kw): + """Add Builders and construction variables for icl to an Environment.""" + return SCons.Tool.intelc.generate(*args, **kw) + +def exists(*args, **kw): + return SCons.Tool.intelc.exists(*args, **kw) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/ifl.py b/scons/scons-local-2.3.0/SCons/Tool/ifl.py new file mode 100644 index 000000000..678a0d14a --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/ifl.py @@ -0,0 +1,72 @@ +"""SCons.Tool.ifl + +Tool-specific initialization for the Intel Fortran compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/ifl.py 2013/03/03 09:48:35 garyo" + +import SCons.Defaults +from SCons.Scanner.Fortran import FortranScan +from FortranCommon import add_all_to_env + +def generate(env): + """Add Builders and construction variables for ifl to an Environment.""" + fscan = FortranScan("FORTRANPATH") + SCons.Tool.SourceFileScanner.add_scanner('.i', fscan) + SCons.Tool.SourceFileScanner.add_scanner('.i90', fscan) + + if 'FORTRANFILESUFFIXES' not in env: + env['FORTRANFILESUFFIXES'] = ['.i'] + else: + env['FORTRANFILESUFFIXES'].append('.i') + + if 'F90FILESUFFIXES' not in env: + env['F90FILESUFFIXES'] = ['.i90'] + else: + env['F90FILESUFFIXES'].append('.i90') + + add_all_to_env(env) + + env['FORTRAN'] = 'ifl' + env['SHFORTRAN'] = '$FORTRAN' + env['FORTRANCOM'] = '$FORTRAN $FORTRANFLAGS $_FORTRANINCFLAGS /c $SOURCES /Fo$TARGET' + env['FORTRANPPCOM'] = '$FORTRAN $FORTRANFLAGS $CPPFLAGS $_CPPDEFFLAGS $_FORTRANINCFLAGS /c $SOURCES /Fo$TARGET' + env['SHFORTRANCOM'] = '$SHFORTRAN $SHFORTRANFLAGS $_FORTRANINCFLAGS /c $SOURCES /Fo$TARGET' + env['SHFORTRANPPCOM'] = '$SHFORTRAN $SHFORTRANFLAGS $CPPFLAGS $_CPPDEFFLAGS $_FORTRANINCFLAGS /c $SOURCES /Fo$TARGET' + +def exists(env): + return env.Detect('ifl') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/ifort.py b/scons/scons-local-2.3.0/SCons/Tool/ifort.py new file mode 100644 index 000000000..80c170ba5 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/ifort.py @@ -0,0 +1,88 @@ +"""SCons.Tool.ifort + +Tool-specific initialization for newer versions of the Intel Fortran Compiler +for Linux/Windows (and possibly Mac OS X). + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/ifort.py 2013/03/03 09:48:35 garyo" + +import SCons.Defaults +from SCons.Scanner.Fortran import FortranScan +from FortranCommon import add_all_to_env + +def generate(env): + """Add Builders and construction variables for ifort to an Environment.""" + # ifort supports Fortran 90 and Fortran 95 + # Additionally, ifort recognizes more file extensions. + fscan = FortranScan("FORTRANPATH") + SCons.Tool.SourceFileScanner.add_scanner('.i', fscan) + SCons.Tool.SourceFileScanner.add_scanner('.i90', fscan) + + if 'FORTRANFILESUFFIXES' not in env: + env['FORTRANFILESUFFIXES'] = ['.i'] + else: + env['FORTRANFILESUFFIXES'].append('.i') + + if 'F90FILESUFFIXES' not in env: + env['F90FILESUFFIXES'] = ['.i90'] + else: + env['F90FILESUFFIXES'].append('.i90') + + add_all_to_env(env) + + fc = 'ifort' + + for dialect in ['F77', 'F90', 'FORTRAN', 'F95']: + env['%s' % dialect] = fc + env['SH%s' % dialect] = '$%s' % dialect + if env['PLATFORM'] == 'posix': + env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS -fPIC' % dialect) + + if env['PLATFORM'] == 'win32': + # On Windows, the ifort compiler specifies the object on the + # command line with -object:, not -o. Massage the necessary + # command-line construction variables. + for dialect in ['F77', 'F90', 'FORTRAN', 'F95']: + for var in ['%sCOM' % dialect, '%sPPCOM' % dialect, + 'SH%sCOM' % dialect, 'SH%sPPCOM' % dialect]: + env[var] = env[var].replace('-o $TARGET', '-object:$TARGET') + env['FORTRANMODDIRPREFIX'] = "/module:" + else: + env['FORTRANMODDIRPREFIX'] = "-module " + +def exists(env): + return env.Detect('ifort') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/ilink.py b/scons/scons-local-2.3.0/SCons/Tool/ilink.py new file mode 100644 index 000000000..7fa9b33b7 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/ilink.py @@ -0,0 +1,59 @@ +"""SCons.Tool.ilink + +Tool-specific initialization for the OS/2 ilink linker. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/ilink.py 2013/03/03 09:48:35 garyo" + +import SCons.Defaults +import SCons.Tool +import SCons.Util + +def generate(env): + """Add Builders and construction variables for ilink to an Environment.""" + SCons.Tool.createProgBuilder(env) + + env['LINK'] = 'ilink' + env['LINKFLAGS'] = SCons.Util.CLVar('') + env['LINKCOM'] = '$LINK $LINKFLAGS /O:$TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' + env['LIBDIRPREFIX']='/LIBPATH:' + env['LIBDIRSUFFIX']='' + env['LIBLINKPREFIX']='' + env['LIBLINKSUFFIX']='$LIBSUFFIX' + +def exists(env): + return env.Detect('ilink') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/ilink32.py b/scons/scons-local-2.3.0/SCons/Tool/ilink32.py new file mode 100644 index 000000000..8b9cef66e --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/ilink32.py @@ -0,0 +1,60 @@ +"""SCons.Tool.ilink32 + +XXX + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/ilink32.py 2013/03/03 09:48:35 garyo" + +import SCons.Tool +import SCons.Tool.bcc32 +import SCons.Util + +def generate(env): + """Add Builders and construction variables for Borland ilink to an + Environment.""" + SCons.Tool.createSharedLibBuilder(env) + SCons.Tool.createProgBuilder(env) + + env['LINK'] = '$CC' + env['LINKFLAGS'] = SCons.Util.CLVar('') + env['LINKCOM'] = '$LINK -q $LINKFLAGS -e$TARGET $SOURCES $LIBS' + env['LIBDIRPREFIX']='' + env['LIBDIRSUFFIX']='' + env['LIBLINKPREFIX']='' + env['LIBLINKSUFFIX']='$LIBSUFFIX' + + +def exists(env): + # Uses bcc32 to do linking as it generally knows where the standard + # LIBS are and set up the linking correctly + return SCons.Tool.bcc32.findIt('bcc32', env) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/install.py b/scons/scons-local-2.3.0/SCons/Tool/install.py new file mode 100644 index 000000000..6f67fac27 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/install.py @@ -0,0 +1,460 @@ +"""SCons.Tool.install + +Tool-specific initialization for the install tool. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/install.py 2013/03/03 09:48:35 garyo" + +import os +import re +import shutil +import stat + +import SCons.Action +from SCons.Util import make_path_relative + +# +# We keep track of *all* installed files. +_INSTALLED_FILES = [] +_UNIQUE_INSTALLED_FILES = None + +class CopytreeError(EnvironmentError): + pass + +# This is a patched version of shutil.copytree from python 2.5. It +# doesn't fail if the dir exists, which regular copytree does +# (annoyingly). Note the XXX comment in the docstring. +def scons_copytree(src, dst, symlinks=False): + """Recursively copy a directory tree using copy2(). + + The destination directory must not already exist. + If exception(s) occur, an CopytreeError is raised with a list of reasons. + + If the optional symlinks flag is true, symbolic links in the + source tree result in symbolic links in the destination tree; if + it is false, the contents of the files pointed to by symbolic + links are copied. + + XXX Consider this example code rather than the ultimate tool. + + """ + names = os.listdir(src) + # garyo@genarts.com fix: check for dir before making dirs. + if not os.path.exists(dst): + os.makedirs(dst) + errors = [] + for name in names: + srcname = os.path.join(src, name) + dstname = os.path.join(dst, name) + try: + if symlinks and os.path.islink(srcname): + linkto = os.readlink(srcname) + os.symlink(linkto, dstname) + elif os.path.isdir(srcname): + scons_copytree(srcname, dstname, symlinks) + else: + shutil.copy2(srcname, dstname) + # XXX What about devices, sockets etc.? + except (IOError, os.error), why: + errors.append((srcname, dstname, str(why))) + # catch the CopytreeError from the recursive copytree so that we can + # continue with other files + except CopytreeError, err: + errors.extend(err.args[0]) + try: + shutil.copystat(src, dst) + except WindowsError: + # can't copy file access times on Windows + pass + except OSError, why: + errors.extend((src, dst, str(why))) + if errors: + raise CopytreeError, errors + + +# +# Functions doing the actual work of the Install Builder. +# +def copyFunc(dest, source, env): + """Install a source file or directory into a destination by copying, + (including copying permission/mode bits).""" + + if os.path.isdir(source): + if os.path.exists(dest): + if not os.path.isdir(dest): + raise SCons.Errors.UserError("cannot overwrite non-directory `%s' with a directory `%s'" % (str(dest), str(source))) + else: + parent = os.path.split(dest)[0] + if not os.path.exists(parent): + os.makedirs(parent) + scons_copytree(source, dest) + else: + shutil.copy2(source, dest) + st = os.stat(source) + os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) + + return 0 + +# +# Functions doing the actual work of the InstallVersionedLib Builder. +# +def copyFuncVersionedLib(dest, source, env): + """Install a versioned library into a destination by copying, + (including copying permission/mode bits) and then creating + required symlinks.""" + + if os.path.isdir(source): + raise SCons.Errors.UserError("cannot install directory `%s' as a version library" % str(source) ) + else: + shutil.copy2(source, dest) + st = os.stat(source) + os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE) + versionedLibLinks(dest, source, env) + + return 0 + +def versionedLibVersion(dest, env): + """Check if dest is a version shared library name. Return version, libname, & install_dir if it is.""" + Verbose = False + platform = env.subst('$PLATFORM') + if not (platform == 'posix' or platform == 'darwin'): + return (None, None, None) + + libname = os.path.basename(dest) + install_dir = os.path.dirname(dest) + shlib_suffix = env.subst('$SHLIBSUFFIX') + # See if the source name is a versioned shared library, get the version number + result = False + + version_re = re.compile("[0-9]+\\.[0-9]+\\.[0-9a-zA-Z]+") + version_File = None + if platform == 'posix': + # handle unix names + versioned_re = re.compile(re.escape(shlib_suffix + '.') + "[0-9]+\\.[0-9]+\\.[0-9a-zA-Z]+") + result = versioned_re.findall(libname) + if result: + version_File = version_re.findall(versioned_re.findall(libname)[-1])[-1] + elif platform == 'darwin': + # handle OSX names + versioned_re = re.compile("\\.[0-9]+\\.[0-9]+\\.[0-9a-zA-Z]+" + re.escape(shlib_suffix) ) + result = versioned_re.findall(libname) + if result: + version_File = version_re.findall(versioned_re.findall(libname)[-1])[-1] + + if Verbose: + print "install: version_File ", version_File + # result is False if we did not find a versioned shared library name, so return and empty list + if not result: + return (None, libname, install_dir) + + version = None + # get version number from the environment + try: + version = env.subst('$SHLIBVERSION') + except KeyError: + version = None + + if version != version_File: + #raise SCons.Errors.UserError("SHLIBVERSION '%s' does not match the version # '%s' in the filename" % (version, version_File) ) + print "SHLIBVERSION '%s' does not match the version # '%s' in the filename, proceeding based on file name" % (version, version_File) + version = version_File + return (version, libname, install_dir) + +def versionedLibLinks(dest, source, env): + """If we are installing a versioned shared library create the required links.""" + Verbose = False + linknames = [] + version, libname, install_dir = versionedLibVersion(dest, env) + + if version != None: + # libname includes the version number if one was given + linknames = SCons.Tool.VersionShLibLinkNames(version,libname,env) + for linkname in linknames: + if Verbose: + print "make link of %s to %s" %(libname, os.path.join(install_dir, linkname)) + fulllinkname = os.path.join(install_dir, linkname) + os.symlink(libname,fulllinkname) + return + +def installFunc(target, source, env): + """Install a source file into a target using the function specified + as the INSTALL construction variable.""" + try: + install = env['INSTALL'] + except KeyError: + raise SCons.Errors.UserError('Missing INSTALL construction variable.') + + assert len(target)==len(source), \ + "Installing source %s into target %s: target and source lists must have same length."%(list(map(str, source)), list(map(str, target))) + for t,s in zip(target,source): + if install(t.get_path(),s.get_path(),env): + return 1 + + return 0 + +def installFuncVersionedLib(target, source, env): + """Install a versioned library into a target using the function specified + as the INSTALLVERSIONEDLIB construction variable.""" + try: + install = env['INSTALLVERSIONEDLIB'] + except KeyError: + raise SCons.Errors.UserError('Missing INSTALLVERSIONEDLIB construction variable.') + + assert len(target)==len(source), \ + "Installing source %s into target %s: target and source lists must have same length."%(list(map(str, source)), list(map(str, target))) + for t,s in zip(target,source): + if install(t.get_path(),s.get_path(),env): + return 1 + + return 0 + +def stringFunc(target, source, env): + installstr = env.get('INSTALLSTR') + if installstr: + return env.subst_target_source(installstr, 0, target, source) + target = str(target[0]) + source = str(source[0]) + if os.path.isdir(source): + type = 'directory' + else: + type = 'file' + return 'Install %s: "%s" as "%s"' % (type, source, target) + +# +# Emitter functions +# +def add_targets_to_INSTALLED_FILES(target, source, env): + """ an emitter that adds all target files to the list stored in the + _INSTALLED_FILES global variable. This way all installed files of one + scons call will be collected. + """ + global _INSTALLED_FILES, _UNIQUE_INSTALLED_FILES + _INSTALLED_FILES.extend(target) + + _UNIQUE_INSTALLED_FILES = None + return (target, source) + +def add_versioned_targets_to_INSTALLED_FILES(target, source, env): + """ an emitter that adds all target files to the list stored in the + _INSTALLED_FILES global variable. This way all installed files of one + scons call will be collected. + """ + global _INSTALLED_FILES, _UNIQUE_INSTALLED_FILES + Verbose = False + _INSTALLED_FILES.extend(target) + + # see if we have a versioned shared library, if so generate side effects + version, libname, install_dir = versionedLibVersion(target[0].path, env) + if version != None: + # generate list of link names + linknames = SCons.Tool.VersionShLibLinkNames(version,libname,env) + for linkname in linknames: + if Verbose: + print "make side effect of %s" % os.path.join(install_dir, linkname) + fulllinkname = os.path.join(install_dir, linkname) + env.SideEffect(fulllinkname,target[0]) + env.Clean(target[0],fulllinkname) + + _UNIQUE_INSTALLED_FILES = None + return (target, source) + +class DESTDIR_factory(object): + """ a node factory, where all files will be relative to the dir supplied + in the constructor. + """ + def __init__(self, env, dir): + self.env = env + self.dir = env.arg2nodes( dir, env.fs.Dir )[0] + + def Entry(self, name): + name = make_path_relative(name) + return self.dir.Entry(name) + + def Dir(self, name): + name = make_path_relative(name) + return self.dir.Dir(name) + +# +# The Builder Definition +# +install_action = SCons.Action.Action(installFunc, stringFunc) +installas_action = SCons.Action.Action(installFunc, stringFunc) +installVerLib_action = SCons.Action.Action(installFuncVersionedLib, stringFunc) + +BaseInstallBuilder = None + +def InstallBuilderWrapper(env, target=None, source=None, dir=None, **kw): + if target and dir: + import SCons.Errors + raise SCons.Errors.UserError("Both target and dir defined for Install(), only one may be defined.") + if not dir: + dir=target + + import SCons.Script + install_sandbox = SCons.Script.GetOption('install_sandbox') + if install_sandbox: + target_factory = DESTDIR_factory(env, install_sandbox) + else: + target_factory = env.fs + + try: + dnodes = env.arg2nodes(dir, target_factory.Dir) + except TypeError: + raise SCons.Errors.UserError("Target `%s' of Install() is a file, but should be a directory. Perhaps you have the Install() arguments backwards?" % str(dir)) + sources = env.arg2nodes(source, env.fs.Entry) + tgt = [] + for dnode in dnodes: + for src in sources: + # Prepend './' so the lookup doesn't interpret an initial + # '#' on the file name portion as meaning the Node should + # be relative to the top-level SConstruct directory. + target = env.fs.Entry('.'+os.sep+src.name, dnode) + #tgt.extend(BaseInstallBuilder(env, target, src, **kw)) + tgt.extend(BaseInstallBuilder(env, target, src, **kw)) + return tgt + +def InstallAsBuilderWrapper(env, target=None, source=None, **kw): + result = [] + for src, tgt in map(lambda x, y: (x, y), source, target): + #result.extend(BaseInstallBuilder(env, tgt, src, **kw)) + result.extend(BaseInstallBuilder(env, tgt, src, **kw)) + return result + +BaseVersionedInstallBuilder = None + +def InstallVersionedBuilderWrapper(env, target=None, source=None, dir=None, **kw): + if target and dir: + import SCons.Errors + raise SCons.Errors.UserError("Both target and dir defined for Install(), only one may be defined.") + if not dir: + dir=target + + import SCons.Script + install_sandbox = SCons.Script.GetOption('install_sandbox') + if install_sandbox: + target_factory = DESTDIR_factory(env, install_sandbox) + else: + target_factory = env.fs + + try: + dnodes = env.arg2nodes(dir, target_factory.Dir) + except TypeError: + raise SCons.Errors.UserError("Target `%s' of Install() is a file, but should be a directory. Perhaps you have the Install() arguments backwards?" % str(dir)) + sources = env.arg2nodes(source, env.fs.Entry) + tgt = [] + for dnode in dnodes: + for src in sources: + # Prepend './' so the lookup doesn't interpret an initial + # '#' on the file name portion as meaning the Node should + # be relative to the top-level SConstruct directory. + target = env.fs.Entry('.'+os.sep+src.name, dnode) + tgt.extend(BaseVersionedInstallBuilder(env, target, src, **kw)) + return tgt + +added = None + +def generate(env): + + from SCons.Script import AddOption, GetOption + global added + if not added: + added = 1 + AddOption('--install-sandbox', + dest='install_sandbox', + type="string", + action="store", + help='A directory under which all installed files will be placed.') + + global BaseInstallBuilder + if BaseInstallBuilder is None: + install_sandbox = GetOption('install_sandbox') + if install_sandbox: + target_factory = DESTDIR_factory(env, install_sandbox) + else: + target_factory = env.fs + + BaseInstallBuilder = SCons.Builder.Builder( + action = install_action, + target_factory = target_factory.Entry, + source_factory = env.fs.Entry, + multi = 1, + emitter = [ add_targets_to_INSTALLED_FILES, ], + name = 'InstallBuilder') + + global BaseVersionedInstallBuilder + if BaseVersionedInstallBuilder is None: + install_sandbox = GetOption('install_sandbox') + if install_sandbox: + target_factory = DESTDIR_factory(env, install_sandbox) + else: + target_factory = env.fs + + BaseVersionedInstallBuilder = SCons.Builder.Builder( + action = installVerLib_action, + target_factory = target_factory.Entry, + source_factory = env.fs.Entry, + multi = 1, + emitter = [ add_versioned_targets_to_INSTALLED_FILES, ], + name = 'InstallVersionedBuilder') + + env['BUILDERS']['_InternalInstall'] = InstallBuilderWrapper + env['BUILDERS']['_InternalInstallAs'] = InstallAsBuilderWrapper + env['BUILDERS']['_InternalInstallVersionedLib'] = InstallVersionedBuilderWrapper + + # We'd like to initialize this doing something like the following, + # but there isn't yet support for a ${SOURCE.type} expansion that + # will print "file" or "directory" depending on what's being + # installed. For now we punt by not initializing it, and letting + # the stringFunc() that we put in the action fall back to the + # hand-crafted default string if it's not set. + # + #try: + # env['INSTALLSTR'] + #except KeyError: + # env['INSTALLSTR'] = 'Install ${SOURCE.type}: "$SOURCES" as "$TARGETS"' + + try: + env['INSTALL'] + except KeyError: + env['INSTALL'] = copyFunc + + try: + env['INSTALLVERSIONEDLIB'] + except KeyError: + env['INSTALLVERSIONEDLIB'] = copyFuncVersionedLib + +def exists(env): + return 1 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/intelc.py b/scons/scons-local-2.3.0/SCons/Tool/intelc.py new file mode 100644 index 000000000..5d267ff97 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/intelc.py @@ -0,0 +1,552 @@ +"""SCons.Tool.icl + +Tool-specific initialization for the Intel C/C++ compiler. +Supports Linux and Windows compilers, v7 and up. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +from __future__ import division + +__revision__ = "src/engine/SCons/Tool/intelc.py 2013/03/03 09:48:35 garyo" + +import math, sys, os.path, glob, string, re + +is_windows = sys.platform == 'win32' +is_win64 = is_windows and (os.environ['PROCESSOR_ARCHITECTURE'] == 'AMD64' or + ('PROCESSOR_ARCHITEW6432' in os.environ and + os.environ['PROCESSOR_ARCHITEW6432'] == 'AMD64')) +is_linux = sys.platform.startswith('linux') +is_mac = sys.platform == 'darwin' + +if is_windows: + import SCons.Tool.msvc +elif is_linux: + import SCons.Tool.gcc +elif is_mac: + import SCons.Tool.gcc +import SCons.Util +import SCons.Warnings + +# Exceptions for this tool +class IntelCError(SCons.Errors.InternalError): + pass +class MissingRegistryError(IntelCError): # missing registry entry + pass +class MissingDirError(IntelCError): # dir not found + pass +class NoRegistryModuleError(IntelCError): # can't read registry at all + pass + +def uniquify(s): + """Return a sequence containing only one copy of each unique element from input sequence s. + Does not preserve order. + Input sequence must be hashable (i.e. must be usable as a dictionary key).""" + u = {} + for x in s: + u[x] = 1 + return list(u.keys()) + +def linux_ver_normalize(vstr): + """Normalize a Linux compiler version number. + Intel changed from "80" to "9.0" in 2005, so we assume if the number + is greater than 60 it's an old-style number and otherwise new-style. + Always returns an old-style float like 80 or 90 for compatibility with Windows. + Shades of Y2K!""" + # Check for version number like 9.1.026: return 91.026 + # XXX needs to be updated for 2011+ versions (like 2011.11.344 which is compiler v12.1.5) + m = re.match(r'([0-9]+)\.([0-9]+)\.([0-9]+)', vstr) + if m: + vmaj,vmin,build = m.groups() + return float(vmaj) * 10. + float(vmin) + float(build) / 1000.; + else: + f = float(vstr) + if is_windows: + return f + else: + if f < 60: return f * 10.0 + else: return f + +def check_abi(abi): + """Check for valid ABI (application binary interface) name, + and map into canonical one""" + if not abi: + return None + abi = abi.lower() + # valid_abis maps input name to canonical name + if is_windows: + valid_abis = {'ia32' : 'ia32', + 'x86' : 'ia32', + 'ia64' : 'ia64', + 'em64t' : 'em64t', + 'amd64' : 'em64t'} + if is_linux: + valid_abis = {'ia32' : 'ia32', + 'x86' : 'ia32', + 'x86_64' : 'x86_64', + 'em64t' : 'x86_64', + 'amd64' : 'x86_64'} + if is_mac: + valid_abis = {'ia32' : 'ia32', + 'x86' : 'ia32', + 'x86_64' : 'x86_64', + 'em64t' : 'x86_64'} + try: + abi = valid_abis[abi] + except KeyError: + raise SCons.Errors.UserError("Intel compiler: Invalid ABI %s, valid values are %s"% \ + (abi, list(valid_abis.keys()))) + return abi + +def vercmp(a, b): + """Compare strings as floats, + but Intel changed Linux naming convention at 9.0""" + return cmp(linux_ver_normalize(b), linux_ver_normalize(a)) + +def get_version_from_list(v, vlist): + """See if we can match v (string) in vlist (list of strings) + Linux has to match in a fuzzy way.""" + if is_windows: + # Simple case, just find it in the list + if v in vlist: return v + else: return None + else: + # Fuzzy match: normalize version number first, but still return + # original non-normalized form. + fuzz = 0.001 + for vi in vlist: + if math.fabs(linux_ver_normalize(vi) - linux_ver_normalize(v)) < fuzz: + return vi + # Not found + return None + +def get_intel_registry_value(valuename, version=None, abi=None): + """ + Return a value from the Intel compiler registry tree. (Windows only) + """ + # Open the key: + if is_win64: + K = 'Software\\Wow6432Node\\Intel\\Compilers\\C++\\' + version + '\\'+abi.upper() + else: + K = 'Software\\Intel\\Compilers\\C++\\' + version + '\\'+abi.upper() + try: + k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, K) + except SCons.Util.RegError: + raise MissingRegistryError("%s was not found in the registry, for Intel compiler version %s, abi='%s'"%(K, version,abi)) + + # Get the value: + try: + v = SCons.Util.RegQueryValueEx(k, valuename)[0] + return v # or v.encode('iso-8859-1', 'replace') to remove unicode? + except SCons.Util.RegError: + raise MissingRegistryError("%s\\%s was not found in the registry."%(K, valuename)) + + +def get_all_compiler_versions(): + """Returns a sorted list of strings, like "70" or "80" or "9.0" + with most recent compiler version first. + """ + versions=[] + if is_windows: + if is_win64: + keyname = 'Software\\WoW6432Node\\Intel\\Compilers\\C++' + else: + keyname = 'Software\\Intel\\Compilers\\C++' + try: + k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, + keyname) + except WindowsError: + return [] + i = 0 + versions = [] + try: + while i < 100: + subkey = SCons.Util.RegEnumKey(k, i) # raises EnvironmentError + # Check that this refers to an existing dir. + # This is not 100% perfect but should catch common + # installation issues like when the compiler was installed + # and then the install directory deleted or moved (rather + # than uninstalling properly), so the registry values + # are still there. + ok = False + for try_abi in ('IA32', 'IA32e', 'IA64', 'EM64T'): + try: + d = get_intel_registry_value('ProductDir', subkey, try_abi) + except MissingRegistryError: + continue # not found in reg, keep going + if os.path.exists(d): ok = True + if ok: + versions.append(subkey) + else: + try: + # Registry points to nonexistent dir. Ignore this + # version. + value = get_intel_registry_value('ProductDir', subkey, 'IA32') + except MissingRegistryError, e: + + # Registry key is left dangling (potentially + # after uninstalling). + + print \ + "scons: *** Ignoring the registry key for the Intel compiler version %s.\n" \ + "scons: *** It seems that the compiler was uninstalled and that the registry\n" \ + "scons: *** was not cleaned up properly.\n" % subkey + else: + print "scons: *** Ignoring "+str(value) + + i = i + 1 + except EnvironmentError: + # no more subkeys + pass + elif is_linux or is_mac: + for d in glob.glob('/opt/intel_cc_*'): + # Typical dir here is /opt/intel_cc_80. + m = re.search(r'cc_(.*)$', d) + if m: + versions.append(m.group(1)) + for d in glob.glob('/opt/intel/cc*/*'): + # Typical dir here is /opt/intel/cc/9.0 for IA32, + # /opt/intel/cce/9.0 for EMT64 (AMD64) + m = re.search(r'([0-9][0-9.]*)$', d) + if m: + versions.append(m.group(1)) + for d in glob.glob('/opt/intel/Compiler/*'): + # Typical dir here is /opt/intel/Compiler/11.1 + m = re.search(r'([0-9][0-9.]*)$', d) + if m: + versions.append(m.group(1)) + for d in glob.glob('/opt/intel/composerxe-*'): + # Typical dir here is /opt/intel/composerxe-2011.4.184 + m = re.search(r'([0-9][0-9.]*)$', d) + if m: + versions.append(m.group(1)) + for d in glob.glob('/opt/intel/composer_xe_*'): + # Typical dir here is /opt/intel/composer_xe_2011_sp1.11.344 + # The _sp1 is useless, the installers are named 2011.9.x, 2011.10.x, 2011.11.x + m = re.search(r'([0-9]{0,4})(?:_sp\d*)?\.([0-9][0-9.]*)$', d) + if m: + versions.append("%s.%s"%(m.group(1), m.group(2))) + def keyfunc(str): + """Given a dot-separated version string, return a tuple of ints representing it.""" + return [int(x) for x in str.split('.')] + # split into ints, sort, then remove dups + return sorted(uniquify(versions), key=keyfunc, reverse=True) + +def get_intel_compiler_top(version, abi): + """ + Return the main path to the top-level dir of the Intel compiler, + using the given version. + The compiler will be in /bin/icl.exe (icc on linux), + the include dir is /include, etc. + """ + + if is_windows: + if not SCons.Util.can_read_reg: + raise NoRegistryModuleError("No Windows registry module was found") + top = get_intel_registry_value('ProductDir', version, abi) + # pre-11, icl was in Bin. 11 and later, it's in Bin/ apparently. + if not os.path.exists(os.path.join(top, "Bin", "icl.exe")) \ + and not os.path.exists(os.path.join(top, "Bin", abi, "icl.exe")): + raise MissingDirError("Can't find Intel compiler in %s"%(top)) + elif is_mac or is_linux: + def find_in_2008style_dir(version): + # first dir is new (>=9.0) style, second is old (8.0) style. + dirs=('/opt/intel/cc/%s', '/opt/intel_cc_%s') + if abi == 'x86_64': + dirs=('/opt/intel/cce/%s',) # 'e' stands for 'em64t', aka x86_64 aka amd64 + top=None + for d in dirs: + if os.path.exists(os.path.join(d%version, "bin", "icc")): + top = d%version + break + return top + def find_in_2010style_dir(version): + dirs=('/opt/intel/Compiler/%s/*'%version) + # typically /opt/intel/Compiler/11.1/064 (then bin/intel64/icc) + dirs=glob.glob(dirs) + # find highest sub-version number by reverse sorting and picking first existing one. + dirs.sort() + dirs.reverse() + top=None + for d in dirs: + if (os.path.exists(os.path.join(d, "bin", "ia32", "icc")) or + os.path.exists(os.path.join(d, "bin", "intel64", "icc"))): + top = d + break + return top + def find_in_2011style_dir(version): + # The 2011 (compiler v12) dirs are inconsistent, so just redo the search from + # get_all_compiler_versions and look for a match (search the newest form first) + top=None + for d in glob.glob('/opt/intel/composer_xe_*'): + # Typical dir here is /opt/intel/composer_xe_2011_sp1.11.344 + # The _sp1 is useless, the installers are named 2011.9.x, 2011.10.x, 2011.11.x + m = re.search(r'([0-9]{0,4})(?:_sp\d*)?\.([0-9][0-9.]*)$', d) + if m: + cur_ver = "%s.%s"%(m.group(1), m.group(2)) + if cur_ver == version and \ + (os.path.exists(os.path.join(d, "bin", "ia32", "icc")) or + os.path.exists(os.path.join(d, "bin", "intel64", "icc"))): + top = d + break + if not top: + for d in glob.glob('/opt/intel/composerxe-*'): + # Typical dir here is /opt/intel/composerxe-2011.4.184 + m = re.search(r'([0-9][0-9.]*)$', d) + if m and m.group(1) == verison and \ + (os.path.exists(os.path.join(d, "bin", "ia32", "icc")) or + os.path.exists(os.path.join(d, "bin", "intel64", "icc"))): + top = d + break + return top + top = find_in_2011style_dir(version) or find_in_2010style_dir(version) or find_in_2008style_dir(version) + # print "INTELC: top=",top + if not top: + raise MissingDirError("Can't find version %s Intel compiler in %s (abi='%s')"%(version,top, abi)) + return top + + +def generate(env, version=None, abi=None, topdir=None, verbose=0): + """Add Builders and construction variables for Intel C/C++ compiler + to an Environment. + args: + version: (string) compiler version to use, like "80" + abi: (string) 'win32' or whatever Itanium version wants + topdir: (string) compiler top dir, like + "c:\Program Files\Intel\Compiler70" + If topdir is used, version and abi are ignored. + verbose: (int) if >0, prints compiler version used. + """ + if not (is_mac or is_linux or is_windows): + # can't handle this platform + return + + if is_windows: + SCons.Tool.msvc.generate(env) + elif is_linux: + SCons.Tool.gcc.generate(env) + elif is_mac: + SCons.Tool.gcc.generate(env) + + # if version is unspecified, use latest + vlist = get_all_compiler_versions() + if not version: + if vlist: + version = vlist[0] + else: + # User may have specified '90' but we need to get actual dirname '9.0'. + # get_version_from_list does that mapping. + v = get_version_from_list(version, vlist) + if not v: + raise SCons.Errors.UserError("Invalid Intel compiler version %s: "%version + \ + "installed versions are %s"%(', '.join(vlist))) + version = v + + # if abi is unspecified, use ia32 + # alternatives are ia64 for Itanium, or amd64 or em64t or x86_64 (all synonyms here) + abi = check_abi(abi) + if abi is None: + if is_mac or is_linux: + # Check if we are on 64-bit linux, default to 64 then. + uname_m = os.uname()[4] + if uname_m == 'x86_64': + abi = 'x86_64' + else: + abi = 'ia32' + else: + if is_win64: + abi = 'em64t' + else: + abi = 'ia32' + + if version and not topdir: + try: + topdir = get_intel_compiler_top(version, abi) + except (SCons.Util.RegError, IntelCError): + topdir = None + + if not topdir: + # Normally this is an error, but it might not be if the compiler is + # on $PATH and the user is importing their env. + class ICLTopDirWarning(SCons.Warnings.Warning): + pass + if (is_mac or is_linux) and not env.Detect('icc') or \ + is_windows and not env.Detect('icl'): + + SCons.Warnings.enableWarningClass(ICLTopDirWarning) + SCons.Warnings.warn(ICLTopDirWarning, + "Failed to find Intel compiler for version='%s', abi='%s'"% + (str(version), str(abi))) + else: + # should be cleaned up to say what this other version is + # since in this case we have some other Intel compiler installed + SCons.Warnings.enableWarningClass(ICLTopDirWarning) + SCons.Warnings.warn(ICLTopDirWarning, + "Can't find Intel compiler top dir for version='%s', abi='%s'"% + (str(version), str(abi))) + + if topdir: + archdir={'x86_64': 'intel64', + 'amd64' : 'intel64', + 'em64t' : 'intel64', + 'x86' : 'ia32', + 'i386' : 'ia32', + 'ia32' : 'ia32' + }[abi] # for v11 and greater + if os.path.exists(os.path.join(topdir, 'bin', archdir)): + bindir="bin/%s"%archdir + libdir="lib/%s"%archdir + else: + bindir="bin" + libdir="lib" + if verbose: + print "Intel C compiler: using version %s (%g), abi %s, in '%s/%s'"%\ + (repr(version), linux_ver_normalize(version),abi,topdir,bindir) + if is_linux: + # Show the actual compiler version by running the compiler. + os.system('%s/%s/icc --version'%(topdir,bindir)) + if is_mac: + # Show the actual compiler version by running the compiler. + os.system('%s/%s/icc --version'%(topdir,bindir)) + + env['INTEL_C_COMPILER_TOP'] = topdir + if is_linux: + paths={'INCLUDE' : 'include', + 'LIB' : libdir, + 'PATH' : bindir, + 'LD_LIBRARY_PATH' : libdir} + for p in paths.keys(): + env.PrependENVPath(p, os.path.join(topdir, paths[p])) + if is_mac: + paths={'INCLUDE' : 'include', + 'LIB' : libdir, + 'PATH' : bindir, + 'LD_LIBRARY_PATH' : libdir} + for p in paths.keys(): + env.PrependENVPath(p, os.path.join(topdir, paths[p])) + if is_windows: + # env key reg valname default subdir of top + paths=(('INCLUDE', 'IncludeDir', 'Include'), + ('LIB' , 'LibDir', 'Lib'), + ('PATH' , 'BinDir', 'Bin')) + # We are supposed to ignore version if topdir is set, so set + # it to the emptry string if it's not already set. + if version is None: + version = '' + # Each path has a registry entry, use that or default to subdir + for p in paths: + try: + path=get_intel_registry_value(p[1], version, abi) + # These paths may have $(ICInstallDir) + # which needs to be substituted with the topdir. + path=path.replace('$(ICInstallDir)', topdir + os.sep) + except IntelCError: + # Couldn't get it from registry: use default subdir of topdir + env.PrependENVPath(p[0], os.path.join(topdir, p[2])) + else: + env.PrependENVPath(p[0], path.split(os.pathsep)) + # print "ICL %s: %s, final=%s"%(p[0], path, str(env['ENV'][p[0]])) + + if is_windows: + env['CC'] = 'icl' + env['CXX'] = 'icl' + env['LINK'] = 'xilink' + else: + env['CC'] = 'icc' + env['CXX'] = 'icpc' + # Don't reset LINK here; + # use smart_link which should already be here from link.py. + #env['LINK'] = '$CC' + env['AR'] = 'xiar' + env['LD'] = 'xild' # not used by default + + # This is not the exact (detailed) compiler version, + # just the major version as determined above or specified + # by the user. It is a float like 80 or 90, in normalized form for Linux + # (i.e. even for Linux 9.0 compiler, still returns 90 rather than 9.0) + if version: + env['INTEL_C_COMPILER_VERSION']=linux_ver_normalize(version) + + if is_windows: + # Look for license file dir + # in system environment, registry, and default location. + envlicdir = os.environ.get("INTEL_LICENSE_FILE", '') + K = ('SOFTWARE\Intel\Licenses') + try: + k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, K) + reglicdir = SCons.Util.RegQueryValueEx(k, "w_cpp")[0] + except (AttributeError, SCons.Util.RegError): + reglicdir = "" + defaultlicdir = r'C:\Program Files\Common Files\Intel\Licenses' + + licdir = None + for ld in [envlicdir, reglicdir]: + # If the string contains an '@', then assume it's a network + # license (port@system) and good by definition. + if ld and (ld.find('@') != -1 or os.path.exists(ld)): + licdir = ld + break + if not licdir: + licdir = defaultlicdir + if not os.path.exists(licdir): + class ICLLicenseDirWarning(SCons.Warnings.Warning): + pass + SCons.Warnings.enableWarningClass(ICLLicenseDirWarning) + SCons.Warnings.warn(ICLLicenseDirWarning, + "Intel license dir was not found." + " Tried using the INTEL_LICENSE_FILE environment variable (%s), the registry (%s) and the default path (%s)." + " Using the default path as a last resort." + % (envlicdir, reglicdir, defaultlicdir)) + env['ENV']['INTEL_LICENSE_FILE'] = licdir + +def exists(env): + if not (is_mac or is_linux or is_windows): + # can't handle this platform + return 0 + + try: + versions = get_all_compiler_versions() + except (SCons.Util.RegError, IntelCError): + versions = None + detected = versions is not None and len(versions) > 0 + if not detected: + # try env.Detect, maybe that will work + if is_windows: + return env.Detect('icl') + elif is_linux: + return env.Detect('icc') + elif is_mac: + return env.Detect('icc') + return detected + +# end of file + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/ipkg.py b/scons/scons-local-2.3.0/SCons/Tool/ipkg.py new file mode 100644 index 000000000..2abc0e639 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/ipkg.py @@ -0,0 +1,67 @@ +"""SCons.Tool.ipkg + +Tool-specific initialization for ipkg. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +The ipkg tool calls the ipkg-build. Its only argument should be the +packages fake_root. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/ipkg.py 2013/03/03 09:48:35 garyo" + +import os + +import SCons.Builder + +def generate(env): + """Add Builders and construction variables for ipkg to an Environment.""" + try: + bld = env['BUILDERS']['Ipkg'] + except KeyError: + bld = SCons.Builder.Builder( action = '$IPKGCOM', + suffix = '$IPKGSUFFIX', + source_scanner = None, + target_scanner = None) + env['BUILDERS']['Ipkg'] = bld + + env['IPKG'] = 'ipkg-build' + env['IPKGCOM'] = '$IPKG $IPKGFLAGS ${SOURCE}' + env['IPKGUSER'] = os.popen('id -un').read().strip() + env['IPKGGROUP'] = os.popen('id -gn').read().strip() + env['IPKGFLAGS'] = SCons.Util.CLVar('-o $IPKGUSER -g $IPKGGROUP') + env['IPKGSUFFIX'] = '.ipk' + +def exists(env): + return env.Detect('ipkg-build') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/jar.py b/scons/scons-local-2.3.0/SCons/Tool/jar.py new file mode 100644 index 000000000..ec8afcac9 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/jar.py @@ -0,0 +1,116 @@ +"""SCons.Tool.jar + +Tool-specific initialization for jar. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/jar.py 2013/03/03 09:48:35 garyo" + +import SCons.Subst +import SCons.Util + +def jarSources(target, source, env, for_signature): + """Only include sources that are not a manifest file.""" + try: + env['JARCHDIR'] + except KeyError: + jarchdir_set = False + else: + jarchdir_set = True + jarchdir = env.subst('$JARCHDIR', target=target, source=source) + if jarchdir: + jarchdir = env.fs.Dir(jarchdir) + result = [] + for src in source: + contents = src.get_text_contents() + if contents[:16] != "Manifest-Version": + if jarchdir_set: + _chdir = jarchdir + else: + try: + _chdir = src.attributes.java_classdir + except AttributeError: + _chdir = None + if _chdir: + # If we are changing the dir with -C, then sources should + # be relative to that directory. + src = SCons.Subst.Literal(src.get_path(_chdir)) + result.append('-C') + result.append(_chdir) + result.append(src) + return result + +def jarManifest(target, source, env, for_signature): + """Look in sources for a manifest file, if any.""" + for src in source: + contents = src.get_text_contents() + if contents[:16] == "Manifest-Version": + return src + return '' + +def jarFlags(target, source, env, for_signature): + """If we have a manifest, make sure that the 'm' + flag is specified.""" + jarflags = env.subst('$JARFLAGS', target=target, source=source) + for src in source: + contents = src.get_text_contents() + if contents[:16] == "Manifest-Version": + if not 'm' in jarflags: + return jarflags + 'm' + break + return jarflags + +def generate(env): + """Add Builders and construction variables for jar to an Environment.""" + SCons.Tool.CreateJarBuilder(env) + + env['JAR'] = 'jar' + env['JARFLAGS'] = SCons.Util.CLVar('cf') + env['_JARFLAGS'] = jarFlags + env['_JARMANIFEST'] = jarManifest + env['_JARSOURCES'] = jarSources + env['_JARCOM'] = '$JAR $_JARFLAGS $TARGET $_JARMANIFEST $_JARSOURCES' + env['JARCOM'] = "${TEMPFILE('$_JARCOM')}" + env['JARSUFFIX'] = '.jar' + +def exists(env): + # As reported by Jan Nijtmans in issue #2730, the simple + # return env.Detect('jar') + # doesn't always work during initialization. For now, we + # stop trying to detect an executable (analogous to the + # javac Builder). + # TODO: Come up with a proper detect() routine...and enable it. + return 1 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/javac.py b/scons/scons-local-2.3.0/SCons/Tool/javac.py new file mode 100644 index 000000000..a039ace5e --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/javac.py @@ -0,0 +1,232 @@ +"""SCons.Tool.javac + +Tool-specific initialization for javac. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Tool/javac.py 2013/03/03 09:48:35 garyo" + +import os +import os.path + +import SCons.Action +import SCons.Builder +from SCons.Node.FS import _my_normcase +from SCons.Tool.JavaCommon import parse_java_file +import SCons.Util + +def classname(path): + """Turn a string (path name) into a Java class name.""" + return os.path.normpath(path).replace(os.sep, '.') + +def emit_java_classes(target, source, env): + """Create and return lists of source java files + and their corresponding target class files. + """ + java_suffix = env.get('JAVASUFFIX', '.java') + class_suffix = env.get('JAVACLASSSUFFIX', '.class') + + target[0].must_be_same(SCons.Node.FS.Dir) + classdir = target[0] + + s = source[0].rentry().disambiguate() + if isinstance(s, SCons.Node.FS.File): + sourcedir = s.dir.rdir() + elif isinstance(s, SCons.Node.FS.Dir): + sourcedir = s.rdir() + else: + raise SCons.Errors.UserError("Java source must be File or Dir, not '%s'" % s.__class__) + + slist = [] + js = _my_normcase(java_suffix) + for entry in source: + entry = entry.rentry().disambiguate() + if isinstance(entry, SCons.Node.FS.File): + slist.append(entry) + elif isinstance(entry, SCons.Node.FS.Dir): + result = SCons.Util.OrderedDict() + dirnode = entry.rdir() + def find_java_files(arg, dirpath, filenames): + java_files = sorted([n for n in filenames + if _my_normcase(n).endswith(js)]) + mydir = dirnode.Dir(dirpath) + java_paths = [mydir.File(f) for f in java_files] + for jp in java_paths: + arg[jp] = True + for dirpath, dirnames, filenames in os.walk(dirnode.get_abspath()): + find_java_files(result, dirpath, filenames) + entry.walk(find_java_files, result) + + slist.extend(list(result.keys())) + else: + raise SCons.Errors.UserError("Java source must be File or Dir, not '%s'" % entry.__class__) + + version = env.get('JAVAVERSION', '1.4') + full_tlist = [] + for f in slist: + tlist = [] + source_file_based = True + pkg_dir = None + if not f.is_derived(): + pkg_dir, classes = parse_java_file(f.rfile().get_abspath(), version) + if classes: + source_file_based = False + if pkg_dir: + d = target[0].Dir(pkg_dir) + p = pkg_dir + os.sep + else: + d = target[0] + p = '' + for c in classes: + t = d.File(c + class_suffix) + t.attributes.java_classdir = classdir + t.attributes.java_sourcedir = sourcedir + t.attributes.java_classname = classname(p + c) + tlist.append(t) + + if source_file_based: + base = f.name[:-len(java_suffix)] + if pkg_dir: + t = target[0].Dir(pkg_dir).File(base + class_suffix) + else: + t = target[0].File(base + class_suffix) + t.attributes.java_classdir = classdir + t.attributes.java_sourcedir = f.dir + t.attributes.java_classname = classname(base) + tlist.append(t) + + for t in tlist: + t.set_specific_source([f]) + + full_tlist.extend(tlist) + + return full_tlist, slist + +JavaAction = SCons.Action.Action('$JAVACCOM', '$JAVACCOMSTR') + +JavaBuilder = SCons.Builder.Builder(action = JavaAction, + emitter = emit_java_classes, + target_factory = SCons.Node.FS.Entry, + source_factory = SCons.Node.FS.Entry) + +class pathopt(object): + """ + Callable object for generating javac-style path options from + a construction variable (e.g. -classpath, -sourcepath). + """ + def __init__(self, opt, var, default=None): + self.opt = opt + self.var = var + self.default = default + + def __call__(self, target, source, env, for_signature): + path = env[self.var] + if path and not SCons.Util.is_List(path): + path = [path] + if self.default: + default = env[self.default] + if default: + if not SCons.Util.is_List(default): + default = [default] + path = path + default + if path: + return [self.opt, os.pathsep.join(map(str, path))] + else: + return [] + +def Java(env, target, source, *args, **kw): + """ + A pseudo-Builder wrapper around the separate JavaClass{File,Dir} + Builders. + """ + if not SCons.Util.is_List(target): + target = [target] + if not SCons.Util.is_List(source): + source = [source] + + # Pad the target list with repetitions of the last element in the + # list so we have a target for every source element. + target = target + ([target[-1]] * (len(source) - len(target))) + + java_suffix = env.subst('$JAVASUFFIX') + result = [] + + for t, s in zip(target, source): + if isinstance(s, SCons.Node.FS.Base): + if isinstance(s, SCons.Node.FS.File): + b = env.JavaClassFile + else: + b = env.JavaClassDir + else: + if os.path.isfile(s): + b = env.JavaClassFile + elif os.path.isdir(s): + b = env.JavaClassDir + elif s[-len(java_suffix):] == java_suffix: + b = env.JavaClassFile + else: + b = env.JavaClassDir + result.extend(b(t, s, *args, **kw)) + + return result + +def generate(env): + """Add Builders and construction variables for javac to an Environment.""" + java_file = SCons.Tool.CreateJavaFileBuilder(env) + java_class = SCons.Tool.CreateJavaClassFileBuilder(env) + java_class_dir = SCons.Tool.CreateJavaClassDirBuilder(env) + java_class.add_emitter(None, emit_java_classes) + java_class.add_emitter(env.subst('$JAVASUFFIX'), emit_java_classes) + java_class_dir.emitter = emit_java_classes + + env.AddMethod(Java) + + env['JAVAC'] = 'javac' + env['JAVACFLAGS'] = SCons.Util.CLVar('') + env['JAVABOOTCLASSPATH'] = [] + env['JAVACLASSPATH'] = [] + env['JAVASOURCEPATH'] = [] + env['_javapathopt'] = pathopt + env['_JAVABOOTCLASSPATH'] = '${_javapathopt("-bootclasspath", "JAVABOOTCLASSPATH")} ' + env['_JAVACLASSPATH'] = '${_javapathopt("-classpath", "JAVACLASSPATH")} ' + env['_JAVASOURCEPATH'] = '${_javapathopt("-sourcepath", "JAVASOURCEPATH", "_JAVASOURCEPATHDEFAULT")} ' + env['_JAVASOURCEPATHDEFAULT'] = '${TARGET.attributes.java_sourcedir}' + env['_JAVACCOM'] = '$JAVAC $JAVACFLAGS $_JAVABOOTCLASSPATH $_JAVACLASSPATH -d ${TARGET.attributes.java_classdir} $_JAVASOURCEPATH $SOURCES' + env['JAVACCOM'] = "${TEMPFILE('$_JAVACCOM')}" + env['JAVACLASSSUFFIX'] = '.class' + env['JAVASUFFIX'] = '.java' + +def exists(env): + return 1 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/javah.py b/scons/scons-local-2.3.0/SCons/Tool/javah.py new file mode 100644 index 000000000..6e8089c6c --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/javah.py @@ -0,0 +1,137 @@ +"""SCons.Tool.javah + +Tool-specific initialization for javah. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/javah.py 2013/03/03 09:48:35 garyo" + +import os.path + +import SCons.Action +import SCons.Builder +import SCons.Node.FS +import SCons.Tool.javac +import SCons.Util + +def emit_java_headers(target, source, env): + """Create and return lists of Java stub header files that will + be created from a set of class files. + """ + class_suffix = env.get('JAVACLASSSUFFIX', '.class') + classdir = env.get('JAVACLASSDIR') + + if not classdir: + try: + s = source[0] + except IndexError: + classdir = '.' + else: + try: + classdir = s.attributes.java_classdir + except AttributeError: + classdir = '.' + classdir = env.Dir(classdir).rdir() + + if str(classdir) == '.': + c_ = None + else: + c_ = str(classdir) + os.sep + + slist = [] + for src in source: + try: + classname = src.attributes.java_classname + except AttributeError: + classname = str(src) + if c_ and classname[:len(c_)] == c_: + classname = classname[len(c_):] + if class_suffix and classname[-len(class_suffix):] == class_suffix: + classname = classname[:-len(class_suffix)] + classname = SCons.Tool.javac.classname(classname) + s = src.rfile() + s.attributes.java_classname = classname + slist.append(s) + + s = source[0].rfile() + if not hasattr(s.attributes, 'java_classdir'): + s.attributes.java_classdir = classdir + + if target[0].__class__ is SCons.Node.FS.File: + tlist = target + else: + if not isinstance(target[0], SCons.Node.FS.Dir): + target[0].__class__ = SCons.Node.FS.Dir + target[0]._morph() + tlist = [] + for s in source: + fname = s.attributes.java_classname.replace('.', '_') + '.h' + t = target[0].File(fname) + t.attributes.java_lookupdir = target[0] + tlist.append(t) + + return tlist, source + +def JavaHOutFlagGenerator(target, source, env, for_signature): + try: + t = target[0] + except (AttributeError, IndexError, TypeError): + t = target + try: + return '-d ' + str(t.attributes.java_lookupdir) + except AttributeError: + return '-o ' + str(t) + +def getJavaHClassPath(env,target, source, for_signature): + path = "${SOURCE.attributes.java_classdir}" + if 'JAVACLASSPATH' in env and env['JAVACLASSPATH']: + path = SCons.Util.AppendPath(path, env['JAVACLASSPATH']) + return "-classpath %s" % (path) + +def generate(env): + """Add Builders and construction variables for javah to an Environment.""" + java_javah = SCons.Tool.CreateJavaHBuilder(env) + java_javah.emitter = emit_java_headers + + env['_JAVAHOUTFLAG'] = JavaHOutFlagGenerator + env['JAVAH'] = 'javah' + env['JAVAHFLAGS'] = SCons.Util.CLVar('') + env['_JAVAHCLASSPATH'] = getJavaHClassPath + env['JAVAHCOM'] = '$JAVAH $JAVAHFLAGS $_JAVAHOUTFLAG $_JAVAHCLASSPATH ${SOURCES.attributes.java_classname}' + env['JAVACLASSSUFFIX'] = '.class' + +def exists(env): + return env.Detect('javah') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/latex.py b/scons/scons-local-2.3.0/SCons/Tool/latex.py new file mode 100644 index 000000000..75033bc13 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/latex.py @@ -0,0 +1,80 @@ +"""SCons.Tool.latex + +Tool-specific initialization for LaTeX. +Generates .dvi files from .latex or .ltx files + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/latex.py 2013/03/03 09:48:35 garyo" + +import SCons.Action +import SCons.Defaults +import SCons.Scanner.LaTeX +import SCons.Util +import SCons.Tool +import SCons.Tool.tex + +def LaTeXAuxFunction(target = None, source= None, env=None): + result = SCons.Tool.tex.InternalLaTeXAuxAction( SCons.Tool.tex.LaTeXAction, target, source, env ) + if result != 0: + SCons.Tool.tex.check_file_error_message(env['LATEX']) + return result + +LaTeXAuxAction = SCons.Action.Action(LaTeXAuxFunction, + strfunction=SCons.Tool.tex.TeXLaTeXStrFunction) + +def generate(env): + """Add Builders and construction variables for LaTeX to an Environment.""" + + env.AppendUnique(LATEXSUFFIXES=SCons.Tool.LaTeXSuffixes) + + import dvi + dvi.generate(env) + + import pdf + pdf.generate(env) + + bld = env['BUILDERS']['DVI'] + bld.add_action('.ltx', LaTeXAuxAction) + bld.add_action('.latex', LaTeXAuxAction) + bld.add_emitter('.ltx', SCons.Tool.tex.tex_eps_emitter) + bld.add_emitter('.latex', SCons.Tool.tex.tex_eps_emitter) + + SCons.Tool.tex.generate_common(env) + +def exists(env): + SCons.Tool.tex.generate_darwin(env) + return env.Detect('latex') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/lex.py b/scons/scons-local-2.3.0/SCons/Tool/lex.py new file mode 100644 index 000000000..63277309f --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/lex.py @@ -0,0 +1,97 @@ +"""SCons.Tool.lex + +Tool-specific initialization for lex. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/lex.py 2013/03/03 09:48:35 garyo" + +import os.path + +import SCons.Action +import SCons.Tool +import SCons.Util + +LexAction = SCons.Action.Action("$LEXCOM", "$LEXCOMSTR") + +def lexEmitter(target, source, env): + sourceBase, sourceExt = os.path.splitext(SCons.Util.to_String(source[0])) + + if sourceExt == ".lm": # If using Objective-C + target = [sourceBase + ".m"] # the extension is ".m". + + # This emitter essentially tries to add to the target all extra + # files generated by flex. + + # Different options that are used to trigger the creation of extra files. + fileGenOptions = ["--header-file=", "--tables-file="] + + lexflags = env.subst("$LEXFLAGS", target=target, source=source) + for option in SCons.Util.CLVar(lexflags): + for fileGenOption in fileGenOptions: + l = len(fileGenOption) + if option[:l] == fileGenOption: + # A file generating option is present, so add the + # file name to the target list. + fileName = option[l:].strip() + target.append(fileName) + return (target, source) + +def generate(env): + """Add Builders and construction variables for lex to an Environment.""" + c_file, cxx_file = SCons.Tool.createCFileBuilders(env) + + # C + c_file.add_action(".l", LexAction) + c_file.add_emitter(".l", lexEmitter) + + c_file.add_action(".lex", LexAction) + c_file.add_emitter(".lex", lexEmitter) + + # Objective-C + cxx_file.add_action(".lm", LexAction) + cxx_file.add_emitter(".lm", lexEmitter) + + # C++ + cxx_file.add_action(".ll", LexAction) + cxx_file.add_emitter(".ll", lexEmitter) + + env["LEX"] = env.Detect("flex") or "lex" + env["LEXFLAGS"] = SCons.Util.CLVar("") + env["LEXCOM"] = "$LEX $LEXFLAGS -t $SOURCES > $TARGET" + +def exists(env): + return env.Detect(["flex", "lex"]) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/link.py b/scons/scons-local-2.3.0/SCons/Tool/link.py new file mode 100644 index 000000000..008a7d799 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/link.py @@ -0,0 +1,185 @@ +"""SCons.Tool.link + +Tool-specific initialization for the generic Posix linker. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/link.py 2013/03/03 09:48:35 garyo" + +import re + +import SCons.Defaults +import SCons.Tool +import SCons.Util +import SCons.Warnings + +from SCons.Tool.FortranCommon import isfortran + +cplusplus = __import__('c++', globals(), locals(), []) + +issued_mixed_link_warning = False + +def smart_link(source, target, env, for_signature): + has_cplusplus = cplusplus.iscplusplus(source) + has_fortran = isfortran(env, source) + if has_cplusplus and has_fortran: + global issued_mixed_link_warning + if not issued_mixed_link_warning: + msg = "Using $CXX to link Fortran and C++ code together.\n\t" + \ + "This may generate a buggy executable if the '%s'\n\t" + \ + "compiler does not know how to deal with Fortran runtimes." + SCons.Warnings.warn(SCons.Warnings.FortranCxxMixWarning, + msg % env.subst('$CXX')) + issued_mixed_link_warning = True + return '$CXX' + elif has_fortran: + return '$FORTRAN' + elif has_cplusplus: + return '$CXX' + return '$CC' + +def shlib_emitter(target, source, env): + Verbose = False + platform = env.subst('$PLATFORM') + for tgt in target: + tgt.attributes.shared = 1 + try: + # target[0] comes in as libtest.so. Add the version extensions + version = env.subst('$SHLIBVERSION') + if version: + version_names = shlib_emitter_names(target, source, env) + # change the name of the target to include the version number + target[0].name = version_names[0] + for name in version_names: + env.SideEffect(name, target[0]) + env.Clean(target[0], name) + if Verbose: + print "shlib_emitter: add side effect - ",name + except KeyError: + version = None + return (target, source) + +def shlib_emitter_names(target, source, env): + """Return list of file names that are side effects for a versioned library build. The first name in the list is the new name for the target""" + Verbose = False + platform = env.subst('$PLATFORM') + version_names = [] + try: + # target[0] comes in as libtest.so. Add the version extensions + version = env.subst('$SHLIBVERSION') + if version.count(".") != 2: + # We need a version of the form x.y.z to proceed + raise ValueError + if version: + if platform == 'posix': + versionparts = version.split('.') + name = target[0].name + # generate library name with the version number + version_name = target[0].name + '.' + version + if Verbose: + print "shlib_emitter_names: target is ", version_name + print "shlib_emitter_names: side effect: ", name + # add version_name to list of names to be a Side effect + version_names.append(version_name) + if Verbose: + print "shlib_emitter_names: versionparts ",versionparts + for ver in versionparts[0:-1]: + name = name + '.' + ver + if Verbose: + print "shlib_emitter_names: side effect: ", name + # add name to list of names to be a Side effect + version_names.append(name) + elif platform == 'darwin': + shlib_suffix = env.subst('$SHLIBSUFFIX') + name = target[0].name + # generate library name with the version number + suffix_re = re.escape(shlib_suffix) + version_name = re.sub(suffix_re, '.' + version + shlib_suffix, name) + if Verbose: + print "shlib_emitter_names: target is ", version_name + print "shlib_emitter_names: side effect: ", name + # add version_name to list of names to be a Side effect + version_names.append(version_name) + except KeyError: + version = None + return version_names + +def generate(env): + """Add Builders and construction variables for gnulink to an Environment.""" + SCons.Tool.createSharedLibBuilder(env) + SCons.Tool.createProgBuilder(env) + + env['SHLINK'] = '$LINK' + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') + env['SHLINKCOM'] = '$SHLINK -o $TARGET $SHLINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' + # don't set up the emitter, cause AppendUnique will generate a list + # starting with None :-( + env.Append(SHLIBEMITTER = [shlib_emitter]) + env['SMARTLINK'] = smart_link + env['LINK'] = "$SMARTLINK" + env['LINKFLAGS'] = SCons.Util.CLVar('') + # __RPATH is only set to something ($_RPATH typically) on platforms that support it. + env['LINKCOM'] = '$LINK -o $TARGET $LINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' + env['LIBDIRPREFIX']='-L' + env['LIBDIRSUFFIX']='' + env['_LIBFLAGS']='${_stripixes(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, LIBPREFIXES, LIBSUFFIXES, __env__)}' + env['LIBLINKPREFIX']='-l' + env['LIBLINKSUFFIX']='' + + if env['PLATFORM'] == 'hpux': + env['SHLIBSUFFIX'] = '.sl' + elif env['PLATFORM'] == 'aix': + env['SHLIBSUFFIX'] = '.a' + + # For most platforms, a loadable module is the same as a shared + # library. Platforms which are different can override these, but + # setting them the same means that LoadableModule works everywhere. + SCons.Tool.createLoadableModuleBuilder(env) + env['LDMODULE'] = '$SHLINK' + # don't set up the emitter, cause AppendUnique will generate a list + # starting with None :-( + env.Append(LDMODULEEMITTER='$SHLIBEMITTER') + env['LDMODULEPREFIX'] = '$SHLIBPREFIX' + env['LDMODULESUFFIX'] = '$SHLIBSUFFIX' + env['LDMODULEFLAGS'] = '$SHLINKFLAGS' + env['LDMODULECOM'] = '$LDMODULE -o $TARGET $LDMODULEFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' + + + +def exists(env): + # This module isn't really a Tool on its own, it's common logic for + # other linkers. + return None + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/linkloc.py b/scons/scons-local-2.3.0/SCons/Tool/linkloc.py new file mode 100644 index 000000000..6fdd4da4f --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/linkloc.py @@ -0,0 +1,112 @@ +"""SCons.Tool.linkloc + +Tool specification for the LinkLoc linker for the Phar Lap ETS embedded +operating system. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/linkloc.py 2013/03/03 09:48:35 garyo" + +import os.path +import re + +import SCons.Action +import SCons.Defaults +import SCons.Errors +import SCons.Tool +import SCons.Util + +from SCons.Tool.MSCommon import msvs_exists, merge_default_version +from SCons.Tool.PharLapCommon import addPharLapPaths + +_re_linker_command = re.compile(r'(\s)@\s*([^\s]+)') + +def repl_linker_command(m): + # Replaces any linker command file directives (e.g. "@foo.lnk") with + # the actual contents of the file. + try: + f=open(m.group(2), "r") + return m.group(1) + f.read() + except IOError: + # the linker should return an error if it can't + # find the linker command file so we will remain quiet. + # However, we will replace the @ with a # so we will not continue + # to find it with recursive substitution + return m.group(1) + '#' + m.group(2) + +class LinklocGenerator(object): + def __init__(self, cmdline): + self.cmdline = cmdline + + def __call__(self, env, target, source, for_signature): + if for_signature: + # Expand the contents of any linker command files recursively + subs = 1 + strsub = env.subst(self.cmdline, target=target, source=source) + while subs: + strsub, subs = _re_linker_command.subn(repl_linker_command, strsub) + return strsub + else: + return "${TEMPFILE('" + self.cmdline + "')}" + +def generate(env): + """Add Builders and construction variables for ar to an Environment.""" + SCons.Tool.createSharedLibBuilder(env) + SCons.Tool.createProgBuilder(env) + + env['SUBST_CMD_FILE'] = LinklocGenerator + env['SHLINK'] = '$LINK' + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS') + env['SHLINKCOM'] = '${SUBST_CMD_FILE("$SHLINK $SHLINKFLAGS $_LIBDIRFLAGS $_LIBFLAGS -dll $TARGET $SOURCES")}' + env['SHLIBEMITTER']= None + env['LINK'] = "linkloc" + env['LINKFLAGS'] = SCons.Util.CLVar('') + env['LINKCOM'] = '${SUBST_CMD_FILE("$LINK $LINKFLAGS $_LIBDIRFLAGS $_LIBFLAGS -exe $TARGET $SOURCES")}' + env['LIBDIRPREFIX']='-libpath ' + env['LIBDIRSUFFIX']='' + env['LIBLINKPREFIX']='-lib ' + env['LIBLINKSUFFIX']='$LIBSUFFIX' + + # Set-up ms tools paths for default version + merge_default_version(env) + + addPharLapPaths(env) + +def exists(env): + if msvs_exists(): + return env.Detect('linkloc') + else: + return 0 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/m4.py b/scons/scons-local-2.3.0/SCons/Tool/m4.py new file mode 100644 index 000000000..dfef65945 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/m4.py @@ -0,0 +1,63 @@ +"""SCons.Tool.m4 + +Tool-specific initialization for m4. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/m4.py 2013/03/03 09:48:35 garyo" + +import SCons.Action +import SCons.Builder +import SCons.Util + +def generate(env): + """Add Builders and construction variables for m4 to an Environment.""" + M4Action = SCons.Action.Action('$M4COM', '$M4COMSTR') + bld = SCons.Builder.Builder(action = M4Action, src_suffix = '.m4') + + env['BUILDERS']['M4'] = bld + + # .m4 files might include other files, and it would be pretty hard + # to write a scanner for it, so let's just cd to the dir of the m4 + # file and run from there. + # The src_suffix setup is like so: file.c.m4 -> file.c, + # file.cpp.m4 -> file.cpp etc. + env['M4'] = 'm4' + env['M4FLAGS'] = SCons.Util.CLVar('-E') + env['M4COM'] = 'cd ${SOURCE.rsrcdir} && $M4 $M4FLAGS < ${SOURCE.file} > ${TARGET.abspath}' + +def exists(env): + return env.Detect('m4') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/masm.py b/scons/scons-local-2.3.0/SCons/Tool/masm.py new file mode 100644 index 000000000..b7fb94ede --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/masm.py @@ -0,0 +1,77 @@ +"""SCons.Tool.masm + +Tool-specific initialization for the Microsoft Assembler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/masm.py 2013/03/03 09:48:35 garyo" + +import SCons.Defaults +import SCons.Tool +import SCons.Util + +ASSuffixes = ['.s', '.asm', '.ASM'] +ASPPSuffixes = ['.spp', '.SPP', '.sx'] +if SCons.Util.case_sensitive_suffixes('.s', '.S'): + ASPPSuffixes.extend(['.S']) +else: + ASSuffixes.extend(['.S']) + +def generate(env): + """Add Builders and construction variables for masm to an Environment.""" + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + for suffix in ASSuffixes: + static_obj.add_action(suffix, SCons.Defaults.ASAction) + shared_obj.add_action(suffix, SCons.Defaults.ASAction) + static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) + shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) + + for suffix in ASPPSuffixes: + static_obj.add_action(suffix, SCons.Defaults.ASPPAction) + shared_obj.add_action(suffix, SCons.Defaults.ASPPAction) + static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) + shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter) + + env['AS'] = 'ml' + env['ASFLAGS'] = SCons.Util.CLVar('/nologo') + env['ASPPFLAGS'] = '$ASFLAGS' + env['ASCOM'] = '$AS $ASFLAGS /c /Fo$TARGET $SOURCES' + env['ASPPCOM'] = '$CC $ASPPFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c /Fo$TARGET $SOURCES' + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 + +def exists(env): + return env.Detect('ml') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/midl.py b/scons/scons-local-2.3.0/SCons/Tool/midl.py new file mode 100644 index 000000000..a69406d1a --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/midl.py @@ -0,0 +1,88 @@ +"""SCons.Tool.midl + +Tool-specific initialization for midl (Microsoft IDL compiler). + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/midl.py 2013/03/03 09:48:35 garyo" + +import SCons.Action +import SCons.Builder +import SCons.Defaults +import SCons.Scanner.IDL +import SCons.Util + +from MSCommon import msvc_exists + +def midl_emitter(target, source, env): + """Produces a list of outputs from the MIDL compiler""" + base, ext = SCons.Util.splitext(str(target[0])) + tlb = target[0] + incl = base + '.h' + interface = base + '_i.c' + t = [tlb, incl, interface] + + midlcom = env['MIDLCOM'] + + if midlcom.find('/proxy') != -1: + proxy = base + '_p.c' + t.append(proxy) + if midlcom.find('/dlldata') != -1: + dlldata = base + '_data.c' + t.append(dlldata) + + return (t,source) + +idl_scanner = SCons.Scanner.IDL.IDLScan() + +midl_action = SCons.Action.Action('$MIDLCOM', '$MIDLCOMSTR') + +midl_builder = SCons.Builder.Builder(action = midl_action, + src_suffix = '.idl', + suffix='.tlb', + emitter = midl_emitter, + source_scanner = idl_scanner) + +def generate(env): + """Add Builders and construction variables for midl to an Environment.""" + + env['MIDL'] = 'MIDL.EXE' + env['MIDLFLAGS'] = SCons.Util.CLVar('/nologo') + env['MIDLCOM'] = '$MIDL $MIDLFLAGS /tlb ${TARGETS[0]} /h ${TARGETS[1]} /iid ${TARGETS[2]} /proxy ${TARGETS[3]} /dlldata ${TARGETS[4]} $SOURCE 2> NUL' + env['BUILDERS']['TypeLibrary'] = midl_builder + +def exists(env): + return msvc_exists() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/mingw.py b/scons/scons-local-2.3.0/SCons/Tool/mingw.py new file mode 100644 index 000000000..a81c94ccf --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/mingw.py @@ -0,0 +1,180 @@ +"""SCons.Tool.gcc + +Tool-specific initialization for MinGW (http://www.mingw.org/) + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/mingw.py 2013/03/03 09:48:35 garyo" + +import os +import os.path + +import SCons.Action +import SCons.Builder +import SCons.Defaults +import SCons.Tool +import SCons.Util + +# This is what we search for to find mingw: +key_program = 'mingw32-gcc' + +def find(env): + # First search in the SCons path + path=env.WhereIs(key_program) + if (path): + return path + # then the OS path: + path=SCons.Util.WhereIs(key_program) + if (path): + return path + + # If that doesn't work try default location for mingw + save_path=env['ENV']['PATH'] + env.AppendENVPath('PATH',r'c:\MinGW\bin') + path =env.WhereIs(key_program) + if not path: + env['ENV']['PATH']=save_path + return path + +def shlib_generator(target, source, env, for_signature): + cmd = SCons.Util.CLVar(['$SHLINK', '$SHLINKFLAGS']) + + dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') + if dll: cmd.extend(['-o', dll]) + + cmd.extend(['$SOURCES', '$_LIBDIRFLAGS', '$_LIBFLAGS']) + + implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX') + if implib: cmd.append('-Wl,--out-implib,'+implib.get_string(for_signature)) + + def_target = env.FindIxes(target, 'WINDOWSDEFPREFIX', 'WINDOWSDEFSUFFIX') + insert_def = env.subst("$WINDOWS_INSERT_DEF") + if not insert_def in ['', '0', 0] and def_target: \ + cmd.append('-Wl,--output-def,'+def_target.get_string(for_signature)) + + return [cmd] + +def shlib_emitter(target, source, env): + dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') + no_import_lib = env.get('no_import_lib', 0) + + if not dll: + raise SCons.Errors.UserError("A shared library should have exactly one target with the suffix: %s" % env.subst("$SHLIBSUFFIX")) + + if not no_import_lib and \ + not env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX'): + + # Create list of target libraries as strings + targetStrings=env.ReplaceIxes(dll, + 'SHLIBPREFIX', 'SHLIBSUFFIX', + 'LIBPREFIX', 'LIBSUFFIX') + + # Now add file nodes to target list + target.append(env.fs.File(targetStrings)) + + # Append a def file target if there isn't already a def file target + # or a def file source or the user has explicitly asked for the target + # to be emitted. + def_source = env.FindIxes(source, 'WINDOWSDEFPREFIX', 'WINDOWSDEFSUFFIX') + def_target = env.FindIxes(target, 'WINDOWSDEFPREFIX', 'WINDOWSDEFSUFFIX') + skip_def_insert = env.subst("$WINDOWS_INSERT_DEF") in ['', '0', 0] + if not def_source and not def_target and not skip_def_insert: + # Create list of target libraries and def files as strings + targetStrings=env.ReplaceIxes(dll, + 'SHLIBPREFIX', 'SHLIBSUFFIX', + 'WINDOWSDEFPREFIX', 'WINDOWSDEFSUFFIX') + + # Now add file nodes to target list + target.append(env.fs.File(targetStrings)) + + return (target, source) + + +shlib_action = SCons.Action.Action(shlib_generator, generator=1) + +res_action = SCons.Action.Action('$RCCOM', '$RCCOMSTR') + +res_builder = SCons.Builder.Builder(action=res_action, suffix='.o', + source_scanner=SCons.Tool.SourceFileScanner) +SCons.Tool.SourceFileScanner.add_scanner('.rc', SCons.Defaults.CScan) + +def generate(env): + mingw = find(env) + if mingw: + dir = os.path.dirname(mingw) + env.PrependENVPath('PATH', dir ) + + + # Most of mingw is the same as gcc and friends... + gnu_tools = ['gcc', 'g++', 'gnulink', 'ar', 'gas', 'gfortran', 'm4'] + for tool in gnu_tools: + SCons.Tool.Tool(tool)(env) + + #... but a few things differ: + env['CC'] = 'gcc' + env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') + env['CXX'] = 'g++' + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') + env['SHLINKCOM'] = shlib_action + env['LDMODULECOM'] = shlib_action + env.Append(SHLIBEMITTER = [shlib_emitter]) + env['AS'] = 'as' + + env['WIN32DEFPREFIX'] = '' + env['WIN32DEFSUFFIX'] = '.def' + env['WINDOWSDEFPREFIX'] = '${WIN32DEFPREFIX}' + env['WINDOWSDEFSUFFIX'] = '${WIN32DEFSUFFIX}' + + env['SHOBJSUFFIX'] = '.o' + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 + + env['RC'] = 'windres' + env['RCFLAGS'] = SCons.Util.CLVar('') + env['RCINCFLAGS'] = '$( ${_concat(RCINCPREFIX, CPPPATH, RCINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' + env['RCINCPREFIX'] = '--include-dir ' + env['RCINCSUFFIX'] = '' + env['RCCOM'] = '$RC $_CPPDEFFLAGS $RCINCFLAGS ${RCINCPREFIX} ${SOURCE.dir} $RCFLAGS -i $SOURCE -o $TARGET' + env['BUILDERS']['RES'] = res_builder + + # Some setting from the platform also have to be overridden: + env['OBJSUFFIX'] = '.o' + env['LIBPREFIX'] = 'lib' + env['LIBSUFFIX'] = '.a' + env['PROGSUFFIX'] = '.exe' + +def exists(env): + return find(env) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/msgfmt.py b/scons/scons-local-2.3.0/SCons/Tool/msgfmt.py new file mode 100644 index 000000000..d444ae21e --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/msgfmt.py @@ -0,0 +1,108 @@ +""" msgfmt tool """ + +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Tool/msgfmt.py 2013/03/03 09:48:35 garyo" + +from SCons.Builder import BuilderBase +############################################################################# +class _MOFileBuilder(BuilderBase): + """ The builder class for `MO` files. + + The reason for this builder to exists and its purpose is quite simillar + as for `_POFileBuilder`. This time, we extend list of sources, not targets, + and call `BuilderBase._execute()` only once (as we assume single-target + here). + """ + + def _execute(self, env, target, source, *args, **kw): + # Here we add support for 'LINGUAS_FILE' keyword. Emitter is not suitable + # in this case, as it is called too late (after multiple sources + # are handled single_source builder. + import SCons.Util + from SCons.Tool.GettextCommon import _read_linguas_from_files + linguas_files = None + if env.has_key('LINGUAS_FILE') and env['LINGUAS_FILE'] is not None: + linguas_files = env['LINGUAS_FILE'] + # This should prevent from endless recursion. + env['LINGUAS_FILE'] = None + # We read only languages. Suffixes shall be added automatically. + linguas = _read_linguas_from_files(env, linguas_files) + if SCons.Util.is_List(source): + source.extend(linguas) + elif source is not None: + source = [source] + linguas + else: + source = linguas + result = BuilderBase._execute(self,env,target,source,*args, **kw) + if linguas_files is not None: + env['LINGUAS_FILE'] = linguas_files + return result +############################################################################# + +############################################################################# +def _create_mo_file_builder(env, **kw): + """ Create builder object for `MOFiles` builder """ + import SCons.Action + # FIXME: What factory use for source? Ours or their? + kw['action'] = SCons.Action.Action('$MSGFMTCOM','$MSGFMTCOMSTR') + kw['suffix'] = '$MOSUFFIX' + kw['src_suffix'] = '$POSUFFIX' + kw['src_builder'] = '_POUpdateBuilder' + kw['single_source'] = True + return _MOFileBuilder(**kw) +############################################################################# + +############################################################################# +def generate(env,**kw): + """ Generate `msgfmt` tool """ + import SCons.Util + from SCons.Tool.GettextCommon import _detect_msgfmt + try: + env['MSGFMT'] = _detect_msgfmt(env) + except: + env['MSGFMT'] = 'msgfmt' + env.SetDefault( + MSGFMTFLAGS = [ SCons.Util.CLVar('-c') ], + MSGFMTCOM = '$MSGFMT $MSGFMTFLAGS -o $TARGET $SOURCE', + MSGFMTCOMSTR = '', + MOSUFFIX = ['.mo'], + POSUFFIX = ['.po'] + ) + env.Append( BUILDERS = { 'MOFiles' : _create_mo_file_builder(env) } ) +############################################################################# + +############################################################################# +def exists(env): + """ Check if the tool exists """ + from SCons.Tool.GettextCommon import _msgfmt_exists + try: + return _msgfmt_exists(env) + except: + return False +############################################################################# + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/msginit.py b/scons/scons-local-2.3.0/SCons/Tool/msginit.py new file mode 100644 index 000000000..fcbd564c0 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/msginit.py @@ -0,0 +1,120 @@ +""" msginit tool + +Tool specific initialization of msginit tool. +""" + +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Tool/msginit.py 2013/03/03 09:48:35 garyo" + +import SCons.Warnings +import SCons.Builder +import re + +############################################################################# +def _optional_no_translator_flag(env): + """ Return '--no-translator' flag if we run *msginit(1)* in non-interactive + mode.""" + import SCons.Util + if env.has_key('POAUTOINIT'): + autoinit = env['POAUTOINIT'] + else: + autoinit = False + if autoinit: + return [SCons.Util.CLVar('--no-translator')] + else: + return [SCons.Util.CLVar('')] +############################################################################# + +############################################################################# +def _POInitBuilder(env, **kw): + """ Create builder object for `POInit` builder. """ + import SCons.Action + from SCons.Tool.GettextCommon import _init_po_files, _POFileBuilder + action = SCons.Action.Action(_init_po_files, None) + return _POFileBuilder(env, action=action, target_alias='$POCREATE_ALIAS') +############################################################################# + +############################################################################# +from SCons.Environment import _null +############################################################################# +def _POInitBuilderWrapper(env, target=None, source=_null, **kw): + """ Wrapper for _POFileBuilder. We use it to make user's life easier. + + This wrapper checks for `$POTDOMAIN` construction variable (or override in + `**kw`) and treats it appropriatelly. + """ + if source is _null: + if 'POTDOMAIN' in kw: + domain = kw['POTDOMAIN'] + elif env.has_key('POTDOMAIN'): + domain = env['POTDOMAIN'] + else: + domain = 'messages' + source = [ domain ] # NOTE: Suffix shall be appended automatically + return env._POInitBuilder(target, source, **kw) +############################################################################# + +############################################################################# +def generate(env,**kw): + """ Generate the `msginit` tool """ + import SCons.Util + from SCons.Tool.GettextCommon import _detect_msginit + try: + env['MSGINIT'] = _detect_msginit(env) + except: + env['MSGINIT'] = 'msginit' + msginitcom = '$MSGINIT ${_MSGNoTranslator(__env__)} -l ${_MSGINITLOCALE}' \ + + ' $MSGINITFLAGS -i $SOURCE -o $TARGET' + # NOTE: We set POTSUFFIX here, in case the 'xgettext' is not loaded + # (sometimes we really don't need it) + env.SetDefault( + POSUFFIX = ['.po'], + POTSUFFIX = ['.pot'], + _MSGINITLOCALE = '${TARGET.filebase}', + _MSGNoTranslator = _optional_no_translator_flag, + MSGINITCOM = msginitcom, + MSGINITCOMSTR = '', + MSGINITFLAGS = [ ], + POAUTOINIT = False, + POCREATE_ALIAS = 'po-create' + ) + env.Append( BUILDERS = { '_POInitBuilder' : _POInitBuilder(env) } ) + env.AddMethod(_POInitBuilderWrapper, 'POInit') + env.AlwaysBuild(env.Alias('$POCREATE_ALIAS')) +############################################################################# + +############################################################################# +def exists(env): + """ Check if the tool exists """ + from SCons.Tool.GettextCommon import _msginit_exists + try: + return _msginit_exists(env) + except: + return False +############################################################################# + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/msgmerge.py b/scons/scons-local-2.3.0/SCons/Tool/msgmerge.py new file mode 100644 index 000000000..35315f939 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/msgmerge.py @@ -0,0 +1,104 @@ +""" msgmerget tool + +Tool specific initialization for `msgmerge` tool. +""" + +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Tool/msgmerge.py 2013/03/03 09:48:35 garyo" + +############################################################################# +def _update_or_init_po_files(target, source, env): + """ Action function for `POUpdate` builder """ + import SCons.Action + from SCons.Tool.GettextCommon import _init_po_files + for tgt in target: + if tgt.rexists(): + action = SCons.Action.Action('$MSGMERGECOM', '$MSGMERGECOMSTR') + else: + action = _init_po_files + status = action([tgt], source, env) + if status : return status + return 0 +############################################################################# + +############################################################################# +def _POUpdateBuilder(env, **kw): + """ Create an object of `POUpdate` builder """ + import SCons.Action + from SCons.Tool.GettextCommon import _POFileBuilder + action = SCons.Action.Action(_update_or_init_po_files, None) + return _POFileBuilder(env, action=action, target_alias='$POUPDATE_ALIAS') +############################################################################# + +############################################################################# +from SCons.Environment import _null +############################################################################# +def _POUpdateBuilderWrapper(env, target=None, source=_null, **kw): + """ Wrapper for `POUpdate` builder - make user's life easier """ + if source is _null: + if 'POTDOMAIN' in kw: + domain = kw['POTDOMAIN'] + elif env.has_key('POTDOMAIN') and env['POTDOMAIN']: + domain = env['POTDOMAIN'] + else: + domain = 'messages' + source = [ domain ] # NOTE: Suffix shall be appended automatically + return env._POUpdateBuilder(target, source, **kw) +############################################################################# + +############################################################################# +def generate(env,**kw): + """ Generate the `xgettext` tool """ + from SCons.Tool.GettextCommon import _detect_msgmerge + try: + env['MSGMERGE'] = _detect_msgmerge(env) + except: + env['MSGMERGE'] = 'msgmerge' + env.SetDefault( + POTSUFFIX = ['.pot'], + POSUFFIX = ['.po'], + MSGMERGECOM = '$MSGMERGE $MSGMERGEFLAGS --update $TARGET $SOURCE', + MSGMERGECOMSTR = '', + MSGMERGEFLAGS = [ ], + POUPDATE_ALIAS = 'po-update' + ) + env.Append(BUILDERS = { '_POUpdateBuilder':_POUpdateBuilder(env) }) + env.AddMethod(_POUpdateBuilderWrapper, 'POUpdate') + env.AlwaysBuild(env.Alias('$POUPDATE_ALIAS')) +############################################################################# + +############################################################################# +def exists(env): + """ Check if the tool exists """ + from SCons.Tool.GettextCommon import _msgmerge_exists + try: + return _msgmerge_exists(env) + except: + return False +############################################################################# + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/mslib.py b/scons/scons-local-2.3.0/SCons/Tool/mslib.py new file mode 100644 index 000000000..b85186728 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/mslib.py @@ -0,0 +1,64 @@ +"""SCons.Tool.mslib + +Tool-specific initialization for lib (MicroSoft library archiver). + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/mslib.py 2013/03/03 09:48:35 garyo" + +import SCons.Defaults +import SCons.Tool +import SCons.Tool.msvs +import SCons.Tool.msvc +import SCons.Util + +from MSCommon import msvc_exists, msvc_setup_env_once + +def generate(env): + """Add Builders and construction variables for lib to an Environment.""" + SCons.Tool.createStaticLibBuilder(env) + + # Set-up ms tools paths + msvc_setup_env_once(env) + + env['AR'] = 'lib' + env['ARFLAGS'] = SCons.Util.CLVar('/nologo') + env['ARCOM'] = "${TEMPFILE('$AR $ARFLAGS /OUT:$TARGET $SOURCES')}" + env['LIBPREFIX'] = '' + env['LIBSUFFIX'] = '.lib' + +def exists(env): + return msvc_exists() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/mslink.py b/scons/scons-local-2.3.0/SCons/Tool/mslink.py new file mode 100644 index 000000000..a35d7dab2 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/mslink.py @@ -0,0 +1,327 @@ +"""SCons.Tool.mslink + +Tool-specific initialization for the Microsoft linker. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/mslink.py 2013/03/03 09:48:35 garyo" + +import os.path + +import SCons.Action +import SCons.Defaults +import SCons.Errors +import SCons.Platform.win32 +import SCons.Tool +import SCons.Tool.msvc +import SCons.Tool.msvs +import SCons.Util + +from MSCommon import msvc_setup_env_once, msvc_exists + +def pdbGenerator(env, target, source, for_signature): + try: + return ['/PDB:%s' % target[0].attributes.pdb, '/DEBUG'] + except (AttributeError, IndexError): + return None + +def _dllTargets(target, source, env, for_signature, paramtp): + listCmd = [] + dll = env.FindIxes(target, '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp) + if dll: listCmd.append("/out:%s"%dll.get_string(for_signature)) + + implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX') + if implib: listCmd.append("/implib:%s"%implib.get_string(for_signature)) + + return listCmd + +def _dllSources(target, source, env, for_signature, paramtp): + listCmd = [] + + deffile = env.FindIxes(source, "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX") + for src in source: + # Check explicitly for a non-None deffile so that the __cmp__ + # method of the base SCons.Util.Proxy class used for some Node + # proxies doesn't try to use a non-existent __dict__ attribute. + if deffile and src == deffile: + # Treat this source as a .def file. + listCmd.append("/def:%s" % src.get_string(for_signature)) + else: + # Just treat it as a generic source file. + listCmd.append(src) + return listCmd + +def windowsShlinkTargets(target, source, env, for_signature): + return _dllTargets(target, source, env, for_signature, 'SHLIB') + +def windowsShlinkSources(target, source, env, for_signature): + return _dllSources(target, source, env, for_signature, 'SHLIB') + +def _windowsLdmodTargets(target, source, env, for_signature): + """Get targets for loadable modules.""" + return _dllTargets(target, source, env, for_signature, 'LDMODULE') + +def _windowsLdmodSources(target, source, env, for_signature): + """Get sources for loadable modules.""" + return _dllSources(target, source, env, for_signature, 'LDMODULE') + +def _dllEmitter(target, source, env, paramtp): + """Common implementation of dll emitter.""" + SCons.Tool.msvc.validate_vars(env) + + extratargets = [] + extrasources = [] + + dll = env.FindIxes(target, '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp) + no_import_lib = env.get('no_import_lib', 0) + + if not dll: + raise SCons.Errors.UserError('A shared library should have exactly one target with the suffix: %s' % env.subst('$%sSUFFIX' % paramtp)) + + insert_def = env.subst("$WINDOWS_INSERT_DEF") + if not insert_def in ['', '0', 0] and \ + not env.FindIxes(source, "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX"): + + # append a def file to the list of sources + extrasources.append( + env.ReplaceIxes(dll, + '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp, + "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX")) + + version_num, suite = SCons.Tool.msvs.msvs_parse_version(env.get('MSVS_VERSION', '6.0')) + if version_num >= 8.0 and \ + (env.get('WINDOWS_INSERT_MANIFEST', 0) or env.get('WINDOWS_EMBED_MANIFEST', 0)): + # MSVC 8 and above automatically generate .manifest files that must be installed + extratargets.append( + env.ReplaceIxes(dll, + '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp, + "WINDOWSSHLIBMANIFESTPREFIX", "WINDOWSSHLIBMANIFESTSUFFIX")) + + if 'PDB' in env and env['PDB']: + pdb = env.arg2nodes('$PDB', target=target, source=source)[0] + extratargets.append(pdb) + target[0].attributes.pdb = pdb + + if not no_import_lib and \ + not env.FindIxes(target, "LIBPREFIX", "LIBSUFFIX"): + # Append an import library to the list of targets. + extratargets.append( + env.ReplaceIxes(dll, + '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp, + "LIBPREFIX", "LIBSUFFIX")) + # and .exp file is created if there are exports from a DLL + extratargets.append( + env.ReplaceIxes(dll, + '%sPREFIX' % paramtp, '%sSUFFIX' % paramtp, + "WINDOWSEXPPREFIX", "WINDOWSEXPSUFFIX")) + + return (target+extratargets, source+extrasources) + +def windowsLibEmitter(target, source, env): + return _dllEmitter(target, source, env, 'SHLIB') + +def ldmodEmitter(target, source, env): + """Emitter for loadable modules. + + Loadable modules are identical to shared libraries on Windows, but building + them is subject to different parameters (LDMODULE*). + """ + return _dllEmitter(target, source, env, 'LDMODULE') + +def prog_emitter(target, source, env): + SCons.Tool.msvc.validate_vars(env) + + extratargets = [] + extrasources = [] + + exe = env.FindIxes(target, "PROGPREFIX", "PROGSUFFIX") + if not exe: + raise SCons.Errors.UserError("An executable should have exactly one target with the suffix: %s" % env.subst("$PROGSUFFIX")) + + version_num, suite = SCons.Tool.msvs.msvs_parse_version(env.get('MSVS_VERSION', '6.0')) + if version_num >= 8.0 and \ + (env.get('WINDOWS_INSERT_MANIFEST', 0) or env.get('WINDOWS_EMBED_MANIFEST', 0)): + # MSVC 8 and above automatically generate .manifest files that have to be installed + extratargets.append( + env.ReplaceIxes(exe, + "PROGPREFIX", "PROGSUFFIX", + "WINDOWSPROGMANIFESTPREFIX", "WINDOWSPROGMANIFESTSUFFIX")) + + if 'PDB' in env and env['PDB']: + pdb = env.arg2nodes('$PDB', target=target, source=source)[0] + extratargets.append(pdb) + target[0].attributes.pdb = pdb + + if version_num >= 11.0 and env.get('PCH', 0): + # MSVC 11 and above need the PCH object file to be added to the link line, + # otherwise you get link error LNK2011. + pchobj = SCons.Util.splitext(str(env['PCH']))[0] + '.obj' + # print "prog_emitter, version %s, appending pchobj %s"%(version_num, pchobj) + if pchobj not in extrasources: + extrasources.append(pchobj) + + return (target+extratargets,source+extrasources) + +def RegServerFunc(target, source, env): + if 'register' in env and env['register']: + ret = regServerAction([target[0]], [source[0]], env) + if ret: + raise SCons.Errors.UserError("Unable to register %s" % target[0]) + else: + print "Registered %s sucessfully" % target[0] + return ret + return 0 + +# These are the actual actions run to embed the manifest. +# They are only called from the Check versions below. +embedManifestExeAction = SCons.Action.Action('$MTEXECOM') +embedManifestDllAction = SCons.Action.Action('$MTSHLIBCOM') + +def embedManifestDllCheck(target, source, env): + """Function run by embedManifestDllCheckAction to check for existence of manifest + and other conditions, and embed the manifest by calling embedManifestDllAction if so.""" + if env.get('WINDOWS_EMBED_MANIFEST', 0): + manifestSrc = target[0].abspath + '.manifest' + if os.path.exists(manifestSrc): + ret = (embedManifestDllAction) ([target[0]],None,env) + if ret: + raise SCons.Errors.UserError, "Unable to embed manifest into %s" % (target[0]) + return ret + else: + print '(embed: no %s.manifest found; not embedding.)'%str(target[0]) + return 0 + +def embedManifestExeCheck(target, source, env): + """Function run by embedManifestExeCheckAction to check for existence of manifest + and other conditions, and embed the manifest by calling embedManifestExeAction if so.""" + if env.get('WINDOWS_EMBED_MANIFEST', 0): + manifestSrc = target[0].abspath + '.manifest' + if os.path.exists(manifestSrc): + ret = (embedManifestExeAction) ([target[0]],None,env) + if ret: + raise SCons.Errors.UserError, "Unable to embed manifest into %s" % (target[0]) + return ret + else: + print '(embed: no %s.manifest found; not embedding.)'%str(target[0]) + return 0 + +embedManifestDllCheckAction = SCons.Action.Action(embedManifestDllCheck, None) +embedManifestExeCheckAction = SCons.Action.Action(embedManifestExeCheck, None) + +regServerAction = SCons.Action.Action("$REGSVRCOM", "$REGSVRCOMSTR") +regServerCheck = SCons.Action.Action(RegServerFunc, None) +shlibLinkAction = SCons.Action.Action('${TEMPFILE("$SHLINK $SHLINKFLAGS $_SHLINK_TARGETS $_LIBDIRFLAGS $_LIBFLAGS $_PDB $_SHLINK_SOURCES")}', '$SHLINKCOMSTR') +compositeShLinkAction = shlibLinkAction + regServerCheck + embedManifestDllCheckAction +ldmodLinkAction = SCons.Action.Action('${TEMPFILE("$LDMODULE $LDMODULEFLAGS $_LDMODULE_TARGETS $_LIBDIRFLAGS $_LIBFLAGS $_PDB $_LDMODULE_SOURCES")}', '$LDMODULECOMSTR') +compositeLdmodAction = ldmodLinkAction + regServerCheck + embedManifestDllCheckAction +exeLinkAction = SCons.Action.Action('${TEMPFILE("$LINK $LINKFLAGS /OUT:$TARGET.windows $_LIBDIRFLAGS $_LIBFLAGS $_PDB $SOURCES.windows")}', '$LINKCOMSTR') +compositeLinkAction = exeLinkAction + embedManifestExeCheckAction + +def generate(env): + """Add Builders and construction variables for ar to an Environment.""" + SCons.Tool.createSharedLibBuilder(env) + SCons.Tool.createProgBuilder(env) + + env['SHLINK'] = '$LINK' + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS /dll') + env['_SHLINK_TARGETS'] = windowsShlinkTargets + env['_SHLINK_SOURCES'] = windowsShlinkSources + env['SHLINKCOM'] = compositeShLinkAction + env.Append(SHLIBEMITTER = [windowsLibEmitter]) + env['LINK'] = 'link' + env['LINKFLAGS'] = SCons.Util.CLVar('/nologo') + env['_PDB'] = pdbGenerator + env['LINKCOM'] = compositeLinkAction + env.Append(PROGEMITTER = [prog_emitter]) + env['LIBDIRPREFIX']='/LIBPATH:' + env['LIBDIRSUFFIX']='' + env['LIBLINKPREFIX']='' + env['LIBLINKSUFFIX']='$LIBSUFFIX' + + env['WIN32DEFPREFIX'] = '' + env['WIN32DEFSUFFIX'] = '.def' + env['WIN32_INSERT_DEF'] = 0 + env['WINDOWSDEFPREFIX'] = '${WIN32DEFPREFIX}' + env['WINDOWSDEFSUFFIX'] = '${WIN32DEFSUFFIX}' + env['WINDOWS_INSERT_DEF'] = '${WIN32_INSERT_DEF}' + + env['WIN32EXPPREFIX'] = '' + env['WIN32EXPSUFFIX'] = '.exp' + env['WINDOWSEXPPREFIX'] = '${WIN32EXPPREFIX}' + env['WINDOWSEXPSUFFIX'] = '${WIN32EXPSUFFIX}' + + env['WINDOWSSHLIBMANIFESTPREFIX'] = '' + env['WINDOWSSHLIBMANIFESTSUFFIX'] = '${SHLIBSUFFIX}.manifest' + env['WINDOWSPROGMANIFESTPREFIX'] = '' + env['WINDOWSPROGMANIFESTSUFFIX'] = '${PROGSUFFIX}.manifest' + + env['REGSVRACTION'] = regServerCheck + env['REGSVR'] = os.path.join(SCons.Platform.win32.get_system_root(),'System32','regsvr32') + env['REGSVRFLAGS'] = '/s ' + env['REGSVRCOM'] = '$REGSVR $REGSVRFLAGS ${TARGET.windows}' + + env['WINDOWS_EMBED_MANIFEST'] = 0 + env['MT'] = 'mt' + #env['MTFLAGS'] = ['-hashupdate'] + env['MTFLAGS'] = SCons.Util.CLVar('/nologo') + # Note: use - here to prevent build failure if no manifest produced. + # This seems much simpler than a fancy system using a function action to see + # if the manifest actually exists before trying to run mt with it. + env['MTEXECOM'] = '-$MT $MTFLAGS -manifest ${TARGET}.manifest $_MANIFEST_SOURCES -outputresource:$TARGET;1' + env['MTSHLIBCOM'] = '-$MT $MTFLAGS -manifest ${TARGET}.manifest $_MANIFEST_SOURCES -outputresource:$TARGET;2' + # Future work garyo 27-Feb-11 + env['_MANIFEST_SOURCES'] = None # _windowsManifestSources + + # Set-up ms tools paths + msvc_setup_env_once(env) + + + # Loadable modules are on Windows the same as shared libraries, but they + # are subject to different build parameters (LDMODULE* variables). + # Therefore LDMODULE* variables correspond as much as possible to + # SHLINK*/SHLIB* ones. + SCons.Tool.createLoadableModuleBuilder(env) + env['LDMODULE'] = '$SHLINK' + env['LDMODULEPREFIX'] = '$SHLIBPREFIX' + env['LDMODULESUFFIX'] = '$SHLIBSUFFIX' + env['LDMODULEFLAGS'] = '$SHLINKFLAGS' + env['_LDMODULE_TARGETS'] = _windowsLdmodTargets + env['_LDMODULE_SOURCES'] = _windowsLdmodSources + env['LDMODULEEMITTER'] = [ldmodEmitter] + env['LDMODULECOM'] = compositeLdmodAction + +def exists(env): + return msvc_exists() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/mssdk.py b/scons/scons-local-2.3.0/SCons/Tool/mssdk.py new file mode 100644 index 000000000..84291bcba --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/mssdk.py @@ -0,0 +1,50 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/mssdk.py 2013/03/03 09:48:35 garyo" + +"""engine.SCons.Tool.mssdk + +Tool-specific initialization for Microsoft SDKs, both Platform +SDKs and Windows SDKs. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +from MSCommon import mssdk_exists, \ + mssdk_setup_env + +def generate(env): + """Add construction variables for an MS SDK to an Environment.""" + mssdk_setup_env(env) + +def exists(env): + return mssdk_exists() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/msvc.py b/scons/scons-local-2.3.0/SCons/Tool/msvc.py new file mode 100644 index 000000000..552c8ef04 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/msvc.py @@ -0,0 +1,278 @@ +"""engine.SCons.Tool.msvc + +Tool-specific initialization for Microsoft Visual C/C++. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/msvc.py 2013/03/03 09:48:35 garyo" + +import os.path +import re +import sys + +import SCons.Action +import SCons.Builder +import SCons.Errors +import SCons.Platform.win32 +import SCons.Tool +import SCons.Tool.msvs +import SCons.Util +import SCons.Warnings +import SCons.Scanner.RC + +from MSCommon import msvc_exists, msvc_setup_env_once + +CSuffixes = ['.c', '.C'] +CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++'] + +def validate_vars(env): + """Validate the PCH and PCHSTOP construction variables.""" + if 'PCH' in env and env['PCH']: + if 'PCHSTOP' not in env: + raise SCons.Errors.UserError("The PCHSTOP construction must be defined if PCH is defined.") + if not SCons.Util.is_String(env['PCHSTOP']): + raise SCons.Errors.UserError("The PCHSTOP construction variable must be a string: %r"%env['PCHSTOP']) + +def pch_emitter(target, source, env): + """Adds the object file target.""" + + validate_vars(env) + + pch = None + obj = None + + for t in target: + if SCons.Util.splitext(str(t))[1] == '.pch': + pch = t + if SCons.Util.splitext(str(t))[1] == '.obj': + obj = t + + if not obj: + obj = SCons.Util.splitext(str(pch))[0]+'.obj' + + target = [pch, obj] # pch must be first, and obj second for the PCHCOM to work + + return (target, source) + +def object_emitter(target, source, env, parent_emitter): + """Sets up the PCH dependencies for an object file.""" + + validate_vars(env) + + parent_emitter(target, source, env) + + # Add a dependency, but only if the target (e.g. 'Source1.obj') + # doesn't correspond to the pre-compiled header ('Source1.pch'). + # If the basenames match, then this was most likely caused by + # someone adding the source file to both the env.PCH() and the + # env.Program() calls, and adding the explicit dependency would + # cause a cycle on the .pch file itself. + # + # See issue #2505 for a discussion of what to do if it turns + # out this assumption causes trouble in the wild: + # http://scons.tigris.org/issues/show_bug.cgi?id=2505 + if 'PCH' in env: + pch = env['PCH'] + if str(target[0]) != SCons.Util.splitext(str(pch))[0] + '.obj': + env.Depends(target, pch) + + return (target, source) + +def static_object_emitter(target, source, env): + return object_emitter(target, source, env, + SCons.Defaults.StaticObjectEmitter) + +def shared_object_emitter(target, source, env): + return object_emitter(target, source, env, + SCons.Defaults.SharedObjectEmitter) + +pch_action = SCons.Action.Action('$PCHCOM', '$PCHCOMSTR') +pch_builder = SCons.Builder.Builder(action=pch_action, suffix='.pch', + emitter=pch_emitter, + source_scanner=SCons.Tool.SourceFileScanner) + + +# Logic to build .rc files into .res files (resource files) +res_scanner = SCons.Scanner.RC.RCScan() +res_action = SCons.Action.Action('$RCCOM', '$RCCOMSTR') +res_builder = SCons.Builder.Builder(action=res_action, + src_suffix='.rc', + suffix='.res', + src_builder=[], + source_scanner=res_scanner) + +def msvc_batch_key(action, env, target, source): + """ + Returns a key to identify unique batches of sources for compilation. + + If batching is enabled (via the $MSVC_BATCH setting), then all + target+source pairs that use the same action, defined by the same + environment, and have the same target and source directories, will + be batched. + + Returning None specifies that the specified target+source should not + be batched with other compilations. + """ + + # Fixing MSVC_BATCH mode. Previous if did not work when MSVC_BATCH + # was set to False. This new version should work better. + # Note we need to do the env.subst so $MSVC_BATCH can be a reference to + # another construction variable, which is why we test for False and 0 + # as strings. + if not 'MSVC_BATCH' in env or env.subst('$MSVC_BATCH') in ('0', 'False', '', None): + # We're not using batching; return no key. + return None + t = target[0] + s = source[0] + if os.path.splitext(t.name)[0] != os.path.splitext(s.name)[0]: + # The base names are different, so this *must* be compiled + # separately; return no key. + return None + return (id(action), id(env), t.dir, s.dir) + +def msvc_output_flag(target, source, env, for_signature): + """ + Returns the correct /Fo flag for batching. + + If batching is disabled or there's only one source file, then we + return an /Fo string that specifies the target explicitly. Otherwise, + we return an /Fo string that just specifies the first target's + directory (where the Visual C/C++ compiler will put the .obj files). + """ + + # Fixing MSVC_BATCH mode. Previous if did not work when MSVC_BATCH + # was set to False. This new version should work better. Removed + # len(source)==1 as batch mode can compile only one file + # (and it also fixed problem with compiling only one changed file + # with batch mode enabled) + if not 'MSVC_BATCH' in env or env.subst('$MSVC_BATCH') in ('0', 'False', '', None): + return '/Fo$TARGET' + else: + # The Visual C/C++ compiler requires a \ at the end of the /Fo + # option to indicate an output directory. We use os.sep here so + # that the test(s) for this can be run on non-Windows systems + # without having a hard-coded backslash mess up command-line + # argument parsing. + return '/Fo${TARGET.dir}' + os.sep + +CAction = SCons.Action.Action("$CCCOM", "$CCCOMSTR", + batch_key=msvc_batch_key, + targets='$CHANGED_TARGETS') +ShCAction = SCons.Action.Action("$SHCCCOM", "$SHCCCOMSTR", + batch_key=msvc_batch_key, + targets='$CHANGED_TARGETS') +CXXAction = SCons.Action.Action("$CXXCOM", "$CXXCOMSTR", + batch_key=msvc_batch_key, + targets='$CHANGED_TARGETS') +ShCXXAction = SCons.Action.Action("$SHCXXCOM", "$SHCXXCOMSTR", + batch_key=msvc_batch_key, + targets='$CHANGED_TARGETS') + +def generate(env): + """Add Builders and construction variables for MSVC++ to an Environment.""" + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + # TODO(batch): shouldn't reach in to cmdgen this way; necessary + # for now to bypass the checks in Builder.DictCmdGenerator.__call__() + # and allow .cc and .cpp to be compiled in the same command line. + static_obj.cmdgen.source_ext_match = False + shared_obj.cmdgen.source_ext_match = False + + for suffix in CSuffixes: + static_obj.add_action(suffix, CAction) + shared_obj.add_action(suffix, ShCAction) + static_obj.add_emitter(suffix, static_object_emitter) + shared_obj.add_emitter(suffix, shared_object_emitter) + + for suffix in CXXSuffixes: + static_obj.add_action(suffix, CXXAction) + shared_obj.add_action(suffix, ShCXXAction) + static_obj.add_emitter(suffix, static_object_emitter) + shared_obj.add_emitter(suffix, shared_object_emitter) + + env['CCPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Z7") or ""}']) + env['CCPCHFLAGS'] = SCons.Util.CLVar(['${(PCH and "/Yu%s \\\"/Fp%s\\\""%(PCHSTOP or "",File(PCH))) or ""}']) + env['_MSVC_OUTPUT_FLAG'] = msvc_output_flag + env['_CCCOMCOM'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $CCPCHFLAGS $CCPDBFLAGS' + env['CC'] = 'cl' + env['CCFLAGS'] = SCons.Util.CLVar('/nologo') + env['CFLAGS'] = SCons.Util.CLVar('') + env['CCCOM'] = '${TEMPFILE("$CC $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CFLAGS $CCFLAGS $_CCCOMCOM")}' + env['SHCC'] = '$CC' + env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') + env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS') + env['SHCCCOM'] = '${TEMPFILE("$SHCC $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCFLAGS $SHCCFLAGS $_CCCOMCOM")}' + env['CXX'] = '$CC' + env['CXXFLAGS'] = SCons.Util.CLVar('$( /TP $)') + env['CXXCOM'] = '${TEMPFILE("$CXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CXXFLAGS $CCFLAGS $_CCCOMCOM")}' + env['SHCXX'] = '$CXX' + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') + env['SHCXXCOM'] = '${TEMPFILE("$SHCXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM")}' + env['CPPDEFPREFIX'] = '/D' + env['CPPDEFSUFFIX'] = '' + env['INCPREFIX'] = '/I' + env['INCSUFFIX'] = '' +# env.Append(OBJEMITTER = [static_object_emitter]) +# env.Append(SHOBJEMITTER = [shared_object_emitter]) + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 + + env['RC'] = 'rc' + env['RCFLAGS'] = SCons.Util.CLVar('') + env['RCSUFFIXES']=['.rc','.rc2'] + env['RCCOM'] = '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES' + env['BUILDERS']['RES'] = res_builder + env['OBJPREFIX'] = '' + env['OBJSUFFIX'] = '.obj' + env['SHOBJPREFIX'] = '$OBJPREFIX' + env['SHOBJSUFFIX'] = '$OBJSUFFIX' + + # Set-up ms tools paths + msvc_setup_env_once(env) + + env['CFILESUFFIX'] = '.c' + env['CXXFILESUFFIX'] = '.cc' + + env['PCHPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Yd") or ""}']) + env['PCHCOM'] = '$CXX /Fo${TARGETS[1]} $CXXFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS $PCHPDBFLAGS' + env['BUILDERS']['PCH'] = pch_builder + + if 'ENV' not in env: + env['ENV'] = {} + if 'SystemRoot' not in env['ENV']: # required for dlls in the winsxs folders + env['ENV']['SystemRoot'] = SCons.Platform.win32.get_system_root() + +def exists(env): + return msvc_exists() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/msvs.py b/scons/scons-local-2.3.0/SCons/Tool/msvs.py new file mode 100644 index 000000000..24f382742 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/msvs.py @@ -0,0 +1,1803 @@ +"""SCons.Tool.msvs + +Tool-specific initialization for Microsoft Visual Studio project files. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Tool/msvs.py 2013/03/03 09:48:35 garyo" + +import SCons.compat + +import base64 +import hashlib +import ntpath +import os +# compat layer imports "cPickle" for us if it's available. +import pickle +import re +import sys + +import SCons.Builder +import SCons.Node.FS +import SCons.Platform.win32 +import SCons.Script.SConscript +import SCons.PathList +import SCons.Util +import SCons.Warnings + +from MSCommon import msvc_exists, msvc_setup_env_once +from SCons.Defaults import processDefines + +############################################################################## +# Below here are the classes and functions for generation of +# DSP/DSW/SLN/VCPROJ files. +############################################################################## + +def xmlify(s): + s = s.replace("&", "&") # do this first + s = s.replace("'", "'") + s = s.replace('"', """) + return s + +# Process a CPPPATH list in includes, given the env, target and source. +# Returns a tuple of nodes. +def processIncludes(includes, env, target, source): + return SCons.PathList.PathList(includes).subst_path(env, target, source) + + +external_makefile_guid = '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}' + +def _generateGUID(slnfile, name): + """This generates a dummy GUID for the sln file to use. It is + based on the MD5 signatures of the sln filename plus the name of + the project. It basically just needs to be unique, and not + change with each invocation.""" + m = hashlib.md5() + # Normalize the slnfile path to a Windows path (\ separators) so + # the generated file has a consistent GUID even if we generate + # it on a non-Windows platform. + m.update(ntpath.normpath(str(slnfile)) + str(name)) + solution = m.hexdigest().upper() + # convert most of the signature to GUID form (discard the rest) + solution = "{" + solution[:8] + "-" + solution[8:12] + "-" + solution[12:16] + "-" + solution[16:20] + "-" + solution[20:32] + "}" + return solution + +version_re = re.compile(r'(\d+\.\d+)(.*)') + +def msvs_parse_version(s): + """ + Split a Visual Studio version, which may in fact be something like + '7.0Exp', into is version number (returned as a float) and trailing + "suite" portion. + """ + num, suite = version_re.match(s).groups() + return float(num), suite + +# os.path.relpath has been introduced in Python 2.6 +# We define it locally for earlier versions of Python +def relpath(path, start=os.path.curdir): + """Return a relative version of a path""" + import sys + if not path: + raise ValueError("no path specified") + start_list = os.path.abspath(start).split(os.sep) + path_list = os.path.abspath(path).split(os.sep) + if 'posix' in sys.builtin_module_names: + # Work out how much of the filepath is shared by start and path. + i = len(os.path.commonprefix([start_list, path_list])) + else: + if start_list[0].lower() != path_list[0].lower(): + unc_path, rest = os.path.splitunc(path) + unc_start, rest = os.path.splitunc(start) + if bool(unc_path) ^ bool(unc_start): + raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)" + % (path, start)) + else: + raise ValueError("path is on drive %s, start on drive %s" + % (path_list[0], start_list[0])) + # Work out how much of the filepath is shared by start and path. + for i in range(min(len(start_list), len(path_list))): + if start_list[i].lower() != path_list[i].lower(): + break + else: + i += 1 + rel_list = [os.pardir] * (len(start_list)-i) + path_list[i:] + if not rel_list: + return os.path.curdir + return os.path.join(*rel_list) + +if not "relpath" in os.path.__all__: + os.path.relpath = relpath + +# This is how we re-invoke SCons from inside MSVS Project files. +# The problem is that we might have been invoked as either scons.bat +# or scons.py. If we were invoked directly as scons.py, then we could +# use sys.argv[0] to find the SCons "executable," but that doesn't work +# if we were invoked as scons.bat, which uses "python -c" to execute +# things and ends up with "-c" as sys.argv[0]. Consequently, we have +# the MSVS Project file invoke SCons the same way that scons.bat does, +# which works regardless of how we were invoked. +def getExecScriptMain(env, xml=None): + scons_home = env.get('SCONS_HOME') + if not scons_home and 'SCONS_LIB_DIR' in os.environ: + scons_home = os.environ['SCONS_LIB_DIR'] + if scons_home: + exec_script_main = "from os.path import join; import sys; sys.path = [ r'%s' ] + sys.path; import SCons.Script; SCons.Script.main()" % scons_home + else: + version = SCons.__version__ + exec_script_main = "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-%(version)s'), join(sys.prefix, 'scons-%(version)s'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons') ] + sys.path; import SCons.Script; SCons.Script.main()" % locals() + if xml: + exec_script_main = xmlify(exec_script_main) + return exec_script_main + +# The string for the Python executable we tell the Project file to use +# is either sys.executable or, if an external PYTHON_ROOT environment +# variable exists, $(PYTHON)ROOT\\python.exe (generalized a little to +# pluck the actual executable name from sys.executable). +try: + python_root = os.environ['PYTHON_ROOT'] +except KeyError: + python_executable = sys.executable +else: + python_executable = os.path.join('$$(PYTHON_ROOT)', + os.path.split(sys.executable)[1]) + +class Config(object): + pass + +def splitFully(path): + dir, base = os.path.split(path) + if dir and dir != '' and dir != path: + return splitFully(dir)+[base] + if base == '': + return [] + return [base] + +def makeHierarchy(sources): + '''Break a list of files into a hierarchy; for each value, if it is a string, + then it is a file. If it is a dictionary, it is a folder. The string is + the original path of the file.''' + + hierarchy = {} + for file in sources: + path = splitFully(file) + if len(path): + dict = hierarchy + for part in path[:-1]: + if part not in dict: + dict[part] = {} + dict = dict[part] + dict[path[-1]] = file + #else: + # print 'Warning: failed to decompose path for '+str(file) + return hierarchy + +class _DSPGenerator(object): + """ Base class for DSP generators """ + + srcargs = [ + 'srcs', + 'incs', + 'localincs', + 'resources', + 'misc'] + + def __init__(self, dspfile, source, env): + self.dspfile = str(dspfile) + try: + get_abspath = dspfile.get_abspath + except AttributeError: + self.dspabs = os.path.abspath(dspfile) + else: + self.dspabs = get_abspath() + + if 'variant' not in env: + raise SCons.Errors.InternalError("You must specify a 'variant' argument (i.e. 'Debug' or " +\ + "'Release') to create an MSVSProject.") + elif SCons.Util.is_String(env['variant']): + variants = [env['variant']] + elif SCons.Util.is_List(env['variant']): + variants = env['variant'] + + if 'buildtarget' not in env or env['buildtarget'] == None: + buildtarget = [''] + elif SCons.Util.is_String(env['buildtarget']): + buildtarget = [env['buildtarget']] + elif SCons.Util.is_List(env['buildtarget']): + if len(env['buildtarget']) != len(variants): + raise SCons.Errors.InternalError("Sizes of 'buildtarget' and 'variant' lists must be the same.") + buildtarget = [] + for bt in env['buildtarget']: + if SCons.Util.is_String(bt): + buildtarget.append(bt) + else: + buildtarget.append(bt.get_abspath()) + else: + buildtarget = [env['buildtarget'].get_abspath()] + if len(buildtarget) == 1: + bt = buildtarget[0] + buildtarget = [] + for _ in variants: + buildtarget.append(bt) + + if 'outdir' not in env or env['outdir'] == None: + outdir = [''] + elif SCons.Util.is_String(env['outdir']): + outdir = [env['outdir']] + elif SCons.Util.is_List(env['outdir']): + if len(env['outdir']) != len(variants): + raise SCons.Errors.InternalError("Sizes of 'outdir' and 'variant' lists must be the same.") + outdir = [] + for s in env['outdir']: + if SCons.Util.is_String(s): + outdir.append(s) + else: + outdir.append(s.get_abspath()) + else: + outdir = [env['outdir'].get_abspath()] + if len(outdir) == 1: + s = outdir[0] + outdir = [] + for v in variants: + outdir.append(s) + + if 'runfile' not in env or env['runfile'] == None: + runfile = buildtarget[-1:] + elif SCons.Util.is_String(env['runfile']): + runfile = [env['runfile']] + elif SCons.Util.is_List(env['runfile']): + if len(env['runfile']) != len(variants): + raise SCons.Errors.InternalError("Sizes of 'runfile' and 'variant' lists must be the same.") + runfile = [] + for s in env['runfile']: + if SCons.Util.is_String(s): + runfile.append(s) + else: + runfile.append(s.get_abspath()) + else: + runfile = [env['runfile'].get_abspath()] + if len(runfile) == 1: + s = runfile[0] + runfile = [] + for v in variants: + runfile.append(s) + + self.sconscript = env['MSVSSCONSCRIPT'] + + cmdargs = env.get('cmdargs', '') + + self.env = env + + if 'name' in self.env: + self.name = self.env['name'] + else: + self.name = os.path.basename(SCons.Util.splitext(self.dspfile)[0]) + self.name = self.env.subst(self.name) + + sourcenames = [ + 'Source Files', + 'Header Files', + 'Local Headers', + 'Resource Files', + 'Other Files'] + + self.sources = {} + for n in sourcenames: + self.sources[n] = [] + + self.configs = {} + + self.nokeep = 0 + if 'nokeep' in env and env['variant'] != 0: + self.nokeep = 1 + + if self.nokeep == 0 and os.path.exists(self.dspabs): + self.Parse() + + for t in zip(sourcenames,self.srcargs): + if t[1] in self.env: + if SCons.Util.is_List(self.env[t[1]]): + for i in self.env[t[1]]: + if not i in self.sources[t[0]]: + self.sources[t[0]].append(i) + else: + if not self.env[t[1]] in self.sources[t[0]]: + self.sources[t[0]].append(self.env[t[1]]) + + for n in sourcenames: + #TODO 2.4: compat layer supports sorted(key=) but not sort(key=) + #TODO 2.4: self.sources[n].sort(key=lambda a: a.lower()) + self.sources[n] = sorted(self.sources[n], key=lambda a: a.lower()) + + def AddConfig(self, variant, buildtarget, outdir, runfile, cmdargs, dspfile=dspfile): + config = Config() + config.buildtarget = buildtarget + config.outdir = outdir + config.cmdargs = cmdargs + config.runfile = runfile + + match = re.match('(.*)\|(.*)', variant) + if match: + config.variant = match.group(1) + config.platform = match.group(2) + else: + config.variant = variant + config.platform = 'Win32' + + self.configs[variant] = config + print "Adding '" + self.name + ' - ' + config.variant + '|' + config.platform + "' to '" + str(dspfile) + "'" + + for i in range(len(variants)): + AddConfig(self, variants[i], buildtarget[i], outdir[i], runfile[i], cmdargs) + + self.platforms = [] + for key in self.configs.keys(): + platform = self.configs[key].platform + if not platform in self.platforms: + self.platforms.append(platform) + + def Build(self): + pass + +V6DSPHeader = """\ +# Microsoft Developer Studio Project File - Name="%(name)s" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) External Target" 0x0106 + +CFG=%(name)s - Win32 %(confkey)s +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "%(name)s.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "%(name)s.mak" CFG="%(name)s - Win32 %(confkey)s" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +""" + +class _GenerateV6DSP(_DSPGenerator): + """Generates a Project file for MSVS 6.0""" + + def PrintHeader(self): + # pick a default config + confkeys = sorted(self.configs.keys()) + + name = self.name + confkey = confkeys[0] + + self.file.write(V6DSPHeader % locals()) + + for kind in confkeys: + self.file.write('!MESSAGE "%s - Win32 %s" (based on "Win32 (x86) External Target")\n' % (name, kind)) + + self.file.write('!MESSAGE \n\n') + + def PrintProject(self): + name = self.name + self.file.write('# Begin Project\n' + '# PROP AllowPerConfigDependencies 0\n' + '# PROP Scc_ProjName ""\n' + '# PROP Scc_LocalPath ""\n\n') + + first = 1 + confkeys = sorted(self.configs.keys()) + for kind in confkeys: + outdir = self.configs[kind].outdir + buildtarget = self.configs[kind].buildtarget + if first == 1: + self.file.write('!IF "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind)) + first = 0 + else: + self.file.write('\n!ELSEIF "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind)) + + env_has_buildtarget = 'MSVSBUILDTARGET' in self.env + if not env_has_buildtarget: + self.env['MSVSBUILDTARGET'] = buildtarget + + # have to write this twice, once with the BASE settings, and once without + for base in ("BASE ",""): + self.file.write('# PROP %sUse_MFC 0\n' + '# PROP %sUse_Debug_Libraries ' % (base, base)) + if kind.lower().find('debug') < 0: + self.file.write('0\n') + else: + self.file.write('1\n') + self.file.write('# PROP %sOutput_Dir "%s"\n' + '# PROP %sIntermediate_Dir "%s"\n' % (base,outdir,base,outdir)) + cmd = 'echo Starting SCons && ' + self.env.subst('$MSVSBUILDCOM', 1) + self.file.write('# PROP %sCmd_Line "%s"\n' + '# PROP %sRebuild_Opt "-c && %s"\n' + '# PROP %sTarget_File "%s"\n' + '# PROP %sBsc_Name ""\n' + '# PROP %sTarget_Dir ""\n'\ + %(base,cmd,base,cmd,base,buildtarget,base,base)) + + if not env_has_buildtarget: + del self.env['MSVSBUILDTARGET'] + + self.file.write('\n!ENDIF\n\n' + '# Begin Target\n\n') + for kind in confkeys: + self.file.write('# Name "%s - Win32 %s"\n' % (name,kind)) + self.file.write('\n') + first = 0 + for kind in confkeys: + if first == 0: + self.file.write('!IF "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind)) + first = 1 + else: + self.file.write('!ELSEIF "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind)) + self.file.write('!ENDIF \n\n') + self.PrintSourceFiles() + self.file.write('# End Target\n' + '# End Project\n') + + if self.nokeep == 0: + # now we pickle some data and add it to the file -- MSDEV will ignore it. + pdata = pickle.dumps(self.configs,1) + pdata = base64.encodestring(pdata) + self.file.write(pdata + '\n') + pdata = pickle.dumps(self.sources,1) + pdata = base64.encodestring(pdata) + self.file.write(pdata + '\n') + + def PrintSourceFiles(self): + categories = {'Source Files': 'cpp|c|cxx|l|y|def|odl|idl|hpj|bat', + 'Header Files': 'h|hpp|hxx|hm|inl', + 'Local Headers': 'h|hpp|hxx|hm|inl', + 'Resource Files': 'r|rc|ico|cur|bmp|dlg|rc2|rct|bin|cnt|rtf|gif|jpg|jpeg|jpe', + 'Other Files': ''} + + for kind in sorted(categories.keys(), key=lambda a: a.lower()): + if not self.sources[kind]: + continue # skip empty groups + + self.file.write('# Begin Group "' + kind + '"\n\n') + typelist = categories[kind].replace('|', ';') + self.file.write('# PROP Default_Filter "' + typelist + '"\n') + + for file in self.sources[kind]: + file = os.path.normpath(file) + self.file.write('# Begin Source File\n\n' + 'SOURCE="' + file + '"\n' + '# End Source File\n') + self.file.write('# End Group\n') + + # add the SConscript file outside of the groups + self.file.write('# Begin Source File\n\n' + 'SOURCE="' + str(self.sconscript) + '"\n' + '# End Source File\n') + + def Parse(self): + try: + dspfile = open(self.dspabs,'r') + except IOError: + return # doesn't exist yet, so can't add anything to configs. + + line = dspfile.readline() + while line: + if line.find("# End Project") > -1: + break + line = dspfile.readline() + + line = dspfile.readline() + datas = line + while line and line != '\n': + line = dspfile.readline() + datas = datas + line + + # OK, we've found our little pickled cache of data. + try: + datas = base64.decodestring(datas) + data = pickle.loads(datas) + except KeyboardInterrupt: + raise + except: + return # unable to unpickle any data for some reason + + self.configs.update(data) + + data = None + line = dspfile.readline() + datas = line + while line and line != '\n': + line = dspfile.readline() + datas = datas + line + + # OK, we've found our little pickled cache of data. + # it has a "# " in front of it, so we strip that. + try: + datas = base64.decodestring(datas) + data = pickle.loads(datas) + except KeyboardInterrupt: + raise + except: + return # unable to unpickle any data for some reason + + self.sources.update(data) + + def Build(self): + try: + self.file = open(self.dspabs,'w') + except IOError, detail: + raise SCons.Errors.InternalError('Unable to open "' + self.dspabs + '" for writing:' + str(detail)) + else: + self.PrintHeader() + self.PrintProject() + self.file.close() + +V7DSPHeader = """\ + + +""" + +V7DSPConfiguration = """\ +\t\t +\t\t\t +\t\t +""" + +V8DSPHeader = """\ + + +""" + +V8DSPConfiguration = """\ +\t\t +\t\t\t +\t\t +""" +class _GenerateV7DSP(_DSPGenerator): + """Generates a Project file for MSVS .NET""" + + def __init__(self, dspfile, source, env): + _DSPGenerator.__init__(self, dspfile, source, env) + self.version = env['MSVS_VERSION'] + self.version_num, self.suite = msvs_parse_version(self.version) + if self.version_num >= 9.0: + self.versionstr = '9.00' + self.dspheader = V8DSPHeader + self.dspconfiguration = V8DSPConfiguration + elif self.version_num >= 8.0: + self.versionstr = '8.00' + self.dspheader = V8DSPHeader + self.dspconfiguration = V8DSPConfiguration + else: + if self.version_num >= 7.1: + self.versionstr = '7.10' + else: + self.versionstr = '7.00' + self.dspheader = V7DSPHeader + self.dspconfiguration = V7DSPConfiguration + self.file = None + + def PrintHeader(self): + env = self.env + versionstr = self.versionstr + name = self.name + encoding = self.env.subst('$MSVSENCODING') + scc_provider = env.get('MSVS_SCC_PROVIDER', '') + scc_project_name = env.get('MSVS_SCC_PROJECT_NAME', '') + scc_aux_path = env.get('MSVS_SCC_AUX_PATH', '') + # MSVS_SCC_LOCAL_PATH is kept for backwards compatibility purpose and should + # be deprecated as soon as possible. + scc_local_path_legacy = env.get('MSVS_SCC_LOCAL_PATH', '') + scc_connection_root = env.get('MSVS_SCC_CONNECTION_ROOT', os.curdir) + scc_local_path = os.path.relpath(scc_connection_root, os.path.dirname(self.dspabs)) + project_guid = env.get('MSVS_PROJECT_GUID', '') + if not project_guid: + project_guid = _generateGUID(self.dspfile, '') + if scc_provider != '': + scc_attrs = '\tSccProjectName="%s"\n' % scc_project_name + if scc_aux_path != '': + scc_attrs += '\tSccAuxPath="%s"\n' % scc_aux_path + scc_attrs += ('\tSccLocalPath="%s"\n' + '\tSccProvider="%s"' % (scc_local_path, scc_provider)) + elif scc_local_path_legacy != '': + # This case is kept for backwards compatibility purpose and should + # be deprecated as soon as possible. + scc_attrs = ('\tSccProjectName="%s"\n' + '\tSccLocalPath="%s"' % (scc_project_name, scc_local_path_legacy)) + else: + self.dspheader = self.dspheader.replace('%(scc_attrs)s\n', '') + + self.file.write(self.dspheader % locals()) + + self.file.write('\t\n') + for platform in self.platforms: + self.file.write( + '\t\t\n' % platform) + self.file.write('\t\n') + + if self.version_num >= 8.0: + self.file.write('\t\n' + '\t\n') + + def PrintProject(self): + self.file.write('\t\n') + + confkeys = sorted(self.configs.keys()) + for kind in confkeys: + variant = self.configs[kind].variant + platform = self.configs[kind].platform + outdir = self.configs[kind].outdir + buildtarget = self.configs[kind].buildtarget + runfile = self.configs[kind].runfile + cmdargs = self.configs[kind].cmdargs + + env_has_buildtarget = 'MSVSBUILDTARGET' in self.env + if not env_has_buildtarget: + self.env['MSVSBUILDTARGET'] = buildtarget + + starting = 'echo Starting SCons && ' + if cmdargs: + cmdargs = ' ' + cmdargs + else: + cmdargs = '' + buildcmd = xmlify(starting + self.env.subst('$MSVSBUILDCOM', 1) + cmdargs) + rebuildcmd = xmlify(starting + self.env.subst('$MSVSREBUILDCOM', 1) + cmdargs) + cleancmd = xmlify(starting + self.env.subst('$MSVSCLEANCOM', 1) + cmdargs) + + # This isn't perfect; CPPDEFINES and CPPPATH can contain $TARGET and $SOURCE, + # so they could vary depending on the command being generated. This code + # assumes they don't. + preprocdefs = xmlify(';'.join(processDefines(self.env.get('CPPDEFINES', [])))) + includepath_Dirs = processIncludes(self.env.get('CPPPATH', []), self.env, None, None) + includepath = xmlify(';'.join([str(x) for x in includepath_Dirs])) + + if not env_has_buildtarget: + del self.env['MSVSBUILDTARGET'] + + self.file.write(self.dspconfiguration % locals()) + + self.file.write('\t\n') + + if self.version_num >= 7.1: + self.file.write('\t\n' + '\t\n') + + self.PrintSourceFiles() + + self.file.write('\n') + + if self.nokeep == 0: + # now we pickle some data and add it to the file -- MSDEV will ignore it. + pdata = pickle.dumps(self.configs,1) + pdata = base64.encodestring(pdata) + self.file.write('\n') + + def printSources(self, hierarchy, commonprefix): + sorteditems = sorted(hierarchy.items(), key=lambda a: a[0].lower()) + + # First folders, then files + for key, value in sorteditems: + if SCons.Util.is_Dict(value): + self.file.write('\t\t\t\n' % (key)) + self.printSources(value, commonprefix) + self.file.write('\t\t\t\n') + + for key, value in sorteditems: + if SCons.Util.is_String(value): + file = value + if commonprefix: + file = os.path.join(commonprefix, value) + file = os.path.normpath(file) + self.file.write('\t\t\t\n' + '\t\t\t\n' % (file)) + + def PrintSourceFiles(self): + categories = {'Source Files': 'cpp;c;cxx;l;y;def;odl;idl;hpj;bat', + 'Header Files': 'h;hpp;hxx;hm;inl', + 'Local Headers': 'h;hpp;hxx;hm;inl', + 'Resource Files': 'r;rc;ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe', + 'Other Files': ''} + + self.file.write('\t\n') + + cats = sorted([k for k in categories.keys() if self.sources[k]], + key=lambda a: a.lower()) + for kind in cats: + if len(cats) > 1: + self.file.write('\t\t\n' % (kind, categories[kind])) + + sources = self.sources[kind] + + # First remove any common prefix + commonprefix = None + s = list(map(os.path.normpath, sources)) + # take the dirname because the prefix may include parts + # of the filenames (e.g. if you have 'dir\abcd' and + # 'dir\acde' then the cp will be 'dir\a' ) + cp = os.path.dirname( os.path.commonprefix(s) ) + if cp and s[0][len(cp)] == os.sep: + # +1 because the filename starts after the separator + sources = [s[len(cp)+1:] for s in sources] + commonprefix = cp + + hierarchy = makeHierarchy(sources) + self.printSources(hierarchy, commonprefix=commonprefix) + + if len(cats)>1: + self.file.write('\t\t\n') + + # add the SConscript file outside of the groups + self.file.write('\t\t\n' + '\t\t\n' % str(self.sconscript)) + + self.file.write('\t\n' + '\t\n' + '\t\n') + + def Parse(self): + try: + dspfile = open(self.dspabs,'r') + except IOError: + return # doesn't exist yet, so can't add anything to configs. + + line = dspfile.readline() + while line: + if line.find('\n') + + def printFilters(self, hierarchy, name): + sorteditems = sorted(hierarchy.items(), key = lambda a: a[0].lower()) + + for key, value in sorteditems: + if SCons.Util.is_Dict(value): + filter_name = name + '\\' + key + self.filters_file.write('\t\t\n' + '\t\t\t%s\n' + '\t\t\n' % (filter_name, _generateGUID(self.dspabs, filter_name))) + self.printFilters(value, filter_name) + + def printSources(self, hierarchy, kind, commonprefix, filter_name): + keywords = {'Source Files': 'ClCompile', + 'Header Files': 'ClInclude', + 'Local Headers': 'ClInclude', + 'Resource Files': 'None', + 'Other Files': 'None'} + + sorteditems = sorted(hierarchy.items(), key = lambda a: a[0].lower()) + + # First folders, then files + for key, value in sorteditems: + if SCons.Util.is_Dict(value): + self.printSources(value, kind, commonprefix, filter_name + '\\' + key) + + for key, value in sorteditems: + if SCons.Util.is_String(value): + file = value + if commonprefix: + file = os.path.join(commonprefix, value) + file = os.path.normpath(file) + + self.file.write('\t\t<%s Include="%s" />\n' % (keywords[kind], file)) + self.filters_file.write('\t\t<%s Include="%s">\n' + '\t\t\t%s\n' + '\t\t\n' % (keywords[kind], file, filter_name, keywords[kind])) + + def PrintSourceFiles(self): + categories = {'Source Files': 'cpp;c;cxx;l;y;def;odl;idl;hpj;bat', + 'Header Files': 'h;hpp;hxx;hm;inl', + 'Local Headers': 'h;hpp;hxx;hm;inl', + 'Resource Files': 'r;rc;ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe', + 'Other Files': ''} + + cats = sorted([k for k in categories.keys() if self.sources[k]], + key = lambda a: a.lower()) + + # print vcxproj.filters file first + self.filters_file.write('\t\n') + for kind in cats: + self.filters_file.write('\t\t\n' + '\t\t\t{7b42d31d-d53c-4868-8b92-ca2bc9fc052f}\n' + '\t\t\t%s\n' + '\t\t\n' % (kind, categories[kind])) + + # First remove any common prefix + sources = self.sources[kind] + commonprefix = None + s = list(map(os.path.normpath, sources)) + # take the dirname because the prefix may include parts + # of the filenames (e.g. if you have 'dir\abcd' and + # 'dir\acde' then the cp will be 'dir\a' ) + cp = os.path.dirname( os.path.commonprefix(s) ) + if cp and s[0][len(cp)] == os.sep: + # +1 because the filename starts after the separator + sources = [s[len(cp)+1:] for s in sources] + commonprefix = cp + + hierarchy = makeHierarchy(sources) + self.printFilters(hierarchy, kind) + + self.filters_file.write('\t\n') + + # then print files and filters + for kind in cats: + self.file.write('\t\n') + self.filters_file.write('\t\n') + + # First remove any common prefix + sources = self.sources[kind] + commonprefix = None + s = list(map(os.path.normpath, sources)) + # take the dirname because the prefix may include parts + # of the filenames (e.g. if you have 'dir\abcd' and + # 'dir\acde' then the cp will be 'dir\a' ) + cp = os.path.dirname( os.path.commonprefix(s) ) + if cp and s[0][len(cp)] == os.sep: + # +1 because the filename starts after the separator + sources = [s[len(cp)+1:] for s in sources] + commonprefix = cp + + hierarchy = makeHierarchy(sources) + self.printSources(hierarchy, kind, commonprefix, kind) + + self.file.write('\t\n') + self.filters_file.write('\t\n') + + # add the SConscript file outside of the groups + self.file.write('\t\n' + '\t\t\n' + #'\t\t\n' + '\t\n' % str(self.sconscript)) + + def Parse(self): + print "_GenerateV10DSP.Parse()" + + def Build(self): + try: + self.file = open(self.dspabs, 'w') + except IOError, detail: + raise SCons.Errors.InternalError('Unable to open "' + self.dspabs + '" for writing:' + str(detail)) + else: + self.PrintHeader() + self.PrintProject() + self.file.close() + +class _DSWGenerator(object): + """ Base class for DSW generators """ + def __init__(self, dswfile, source, env): + self.dswfile = os.path.normpath(str(dswfile)) + self.dsw_folder_path = os.path.dirname(os.path.abspath(self.dswfile)) + self.env = env + + if 'projects' not in env: + raise SCons.Errors.UserError("You must specify a 'projects' argument to create an MSVSSolution.") + projects = env['projects'] + if not SCons.Util.is_List(projects): + raise SCons.Errors.InternalError("The 'projects' argument must be a list of nodes.") + projects = SCons.Util.flatten(projects) + if len(projects) < 1: + raise SCons.Errors.UserError("You must specify at least one project to create an MSVSSolution.") + self.dspfiles = list(map(str, projects)) + + if 'name' in self.env: + self.name = self.env['name'] + else: + self.name = os.path.basename(SCons.Util.splitext(self.dswfile)[0]) + self.name = self.env.subst(self.name) + + def Build(self): + pass + +class _GenerateV7DSW(_DSWGenerator): + """Generates a Solution file for MSVS .NET""" + def __init__(self, dswfile, source, env): + _DSWGenerator.__init__(self, dswfile, source, env) + + self.file = None + self.version = self.env['MSVS_VERSION'] + self.version_num, self.suite = msvs_parse_version(self.version) + self.versionstr = '7.00' + if self.version_num >= 11.0: + self.versionstr = '12.00' + elif self.version_num >= 10.0: + self.versionstr = '11.00' + elif self.version_num >= 9.0: + self.versionstr = '10.00' + elif self.version_num >= 8.0: + self.versionstr = '9.00' + elif self.version_num >= 7.1: + self.versionstr = '8.00' + + if 'slnguid' in env and env['slnguid']: + self.slnguid = env['slnguid'] + else: + self.slnguid = _generateGUID(dswfile, self.name) + + self.configs = {} + + self.nokeep = 0 + if 'nokeep' in env and env['variant'] != 0: + self.nokeep = 1 + + if self.nokeep == 0 and os.path.exists(self.dswfile): + self.Parse() + + def AddConfig(self, variant, dswfile=dswfile): + config = Config() + + match = re.match('(.*)\|(.*)', variant) + if match: + config.variant = match.group(1) + config.platform = match.group(2) + else: + config.variant = variant + config.platform = 'Win32' + + self.configs[variant] = config + print "Adding '" + self.name + ' - ' + config.variant + '|' + config.platform + "' to '" + str(dswfile) + "'" + + if 'variant' not in env: + raise SCons.Errors.InternalError("You must specify a 'variant' argument (i.e. 'Debug' or " +\ + "'Release') to create an MSVS Solution File.") + elif SCons.Util.is_String(env['variant']): + AddConfig(self, env['variant']) + elif SCons.Util.is_List(env['variant']): + for variant in env['variant']: + AddConfig(self, variant) + + self.platforms = [] + for key in self.configs.keys(): + platform = self.configs[key].platform + if not platform in self.platforms: + self.platforms.append(platform) + + def GenerateProjectFilesInfo(self): + for dspfile in self.dspfiles: + dsp_folder_path, name = os.path.split(dspfile) + dsp_folder_path = os.path.abspath(dsp_folder_path) + dsp_relative_folder_path = os.path.relpath(dsp_folder_path, self.dsw_folder_path) + if dsp_relative_folder_path == os.curdir: + dsp_relative_file_path = name + else: + dsp_relative_file_path = os.path.join(dsp_relative_folder_path, name) + dspfile_info = {'NAME': name, + 'GUID': _generateGUID(dspfile, ''), + 'FOLDER_PATH': dsp_folder_path, + 'FILE_PATH': dspfile, + 'SLN_RELATIVE_FOLDER_PATH': dsp_relative_folder_path, + 'SLN_RELATIVE_FILE_PATH': dsp_relative_file_path} + self.dspfiles_info.append(dspfile_info) + + self.dspfiles_info = [] + GenerateProjectFilesInfo(self) + + def Parse(self): + try: + dswfile = open(self.dswfile,'r') + except IOError: + return # doesn't exist yet, so can't add anything to configs. + + line = dswfile.readline() + while line: + if line[:9] == "EndGlobal": + break + line = dswfile.readline() + + line = dswfile.readline() + datas = line + while line: + line = dswfile.readline() + datas = datas + line + + # OK, we've found our little pickled cache of data. + try: + datas = base64.decodestring(datas) + data = pickle.loads(datas) + except KeyboardInterrupt: + raise + except: + return # unable to unpickle any data for some reason + + self.configs.update(data) + + def PrintSolution(self): + """Writes a solution file""" + self.file.write('Microsoft Visual Studio Solution File, Format Version %s\n' % self.versionstr) + if self.version_num >= 11.0: + self.file.write('# Visual Studio 11\n') + elif self.version_num >= 10.0: + self.file.write('# Visual Studio 2010\n') + elif self.version_num >= 9.0: + self.file.write('# Visual Studio 2008\n') + elif self.version_num >= 8.0: + self.file.write('# Visual Studio 2005\n') + + for dspinfo in self.dspfiles_info: + name = dspinfo['NAME'] + base, suffix = SCons.Util.splitext(name) + if suffix == '.vcproj': + name = base + self.file.write('Project("%s") = "%s", "%s", "%s"\n' + % (external_makefile_guid, name, dspinfo['SLN_RELATIVE_FILE_PATH'], dspinfo['GUID'])) + if self.version_num >= 7.1 and self.version_num < 8.0: + self.file.write('\tProjectSection(ProjectDependencies) = postProject\n' + '\tEndProjectSection\n') + self.file.write('EndProject\n') + + self.file.write('Global\n') + + env = self.env + if 'MSVS_SCC_PROVIDER' in env: + scc_number_of_projects = len(self.dspfiles) + 1 + slnguid = self.slnguid + scc_provider = env.get('MSVS_SCC_PROVIDER', '').replace(' ', r'\u0020') + scc_project_name = env.get('MSVS_SCC_PROJECT_NAME', '').replace(' ', r'\u0020') + scc_connection_root = env.get('MSVS_SCC_CONNECTION_ROOT', os.curdir) + scc_local_path = os.path.relpath(scc_connection_root, self.dsw_folder_path).replace('\\', '\\\\') + self.file.write('\tGlobalSection(SourceCodeControl) = preSolution\n' + '\t\tSccNumberOfProjects = %(scc_number_of_projects)d\n' + '\t\tSccProjectName0 = %(scc_project_name)s\n' + '\t\tSccLocalPath0 = %(scc_local_path)s\n' + '\t\tSccProvider0 = %(scc_provider)s\n' + '\t\tCanCheckoutShared = true\n' % locals()) + sln_relative_path_from_scc = os.path.relpath(self.dsw_folder_path, scc_connection_root) + if sln_relative_path_from_scc != os.curdir: + self.file.write('\t\tSccProjectFilePathRelativizedFromConnection0 = %s\\\\\n' + % sln_relative_path_from_scc.replace('\\', '\\\\')) + if self.version_num < 8.0: + # When present, SolutionUniqueID is automatically removed by VS 2005 + # TODO: check for Visual Studio versions newer than 2005 + self.file.write('\t\tSolutionUniqueID = %s\n' % slnguid) + for dspinfo in self.dspfiles_info: + i = self.dspfiles_info.index(dspinfo) + 1 + dsp_relative_file_path = dspinfo['SLN_RELATIVE_FILE_PATH'].replace('\\', '\\\\') + dsp_scc_relative_folder_path = os.path.relpath(dspinfo['FOLDER_PATH'], scc_connection_root).replace('\\', '\\\\') + self.file.write('\t\tSccProjectUniqueName%(i)s = %(dsp_relative_file_path)s\n' + '\t\tSccLocalPath%(i)d = %(scc_local_path)s\n' + '\t\tCanCheckoutShared = true\n' + '\t\tSccProjectFilePathRelativizedFromConnection%(i)s = %(dsp_scc_relative_folder_path)s\\\\\n' + % locals()) + self.file.write('\tEndGlobalSection\n') + if self.version_num >= 8.0: + self.file.write('\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n') + else: + self.file.write('\tGlobalSection(SolutionConfiguration) = preSolution\n') + + confkeys = sorted(self.configs.keys()) + cnt = 0 + for name in confkeys: + variant = self.configs[name].variant + platform = self.configs[name].platform + if self.version_num >= 8.0: + self.file.write('\t\t%s|%s = %s|%s\n' % (variant, platform, variant, platform)) + else: + self.file.write('\t\tConfigName.%d = %s\n' % (cnt, variant)) + cnt = cnt + 1 + self.file.write('\tEndGlobalSection\n') + if self.version_num <= 7.1: + self.file.write('\tGlobalSection(ProjectDependencies) = postSolution\n' + '\tEndGlobalSection\n') + if self.version_num >= 8.0: + self.file.write('\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n') + else: + self.file.write('\tGlobalSection(ProjectConfiguration) = postSolution\n') + + for name in confkeys: + variant = self.configs[name].variant + platform = self.configs[name].platform + if self.version_num >= 8.0: + for dspinfo in self.dspfiles_info: + guid = dspinfo['GUID'] + self.file.write('\t\t%s.%s|%s.ActiveCfg = %s|%s\n' + '\t\t%s.%s|%s.Build.0 = %s|%s\n' % (guid,variant,platform,variant,platform,guid,variant,platform,variant,platform)) + else: + for dspinfo in self.dspfiles_info: + guid = dspinfo['GUID'] + self.file.write('\t\t%s.%s.ActiveCfg = %s|%s\n' + '\t\t%s.%s.Build.0 = %s|%s\n' %(guid,variant,variant,platform,guid,variant,variant,platform)) + + self.file.write('\tEndGlobalSection\n') + + if self.version_num >= 8.0: + self.file.write('\tGlobalSection(SolutionProperties) = preSolution\n' + '\t\tHideSolutionNode = FALSE\n' + '\tEndGlobalSection\n') + else: + self.file.write('\tGlobalSection(ExtensibilityGlobals) = postSolution\n' + '\tEndGlobalSection\n' + '\tGlobalSection(ExtensibilityAddIns) = postSolution\n' + '\tEndGlobalSection\n') + self.file.write('EndGlobal\n') + if self.nokeep == 0: + pdata = pickle.dumps(self.configs,1) + pdata = base64.encodestring(pdata) + self.file.write(pdata + '\n') + + def Build(self): + try: + self.file = open(self.dswfile,'w') + except IOError, detail: + raise SCons.Errors.InternalError('Unable to open "' + self.dswfile + '" for writing:' + str(detail)) + else: + self.PrintSolution() + self.file.close() + +V6DSWHeader = """\ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "%(name)s"="%(dspfile)s" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### +""" + +class _GenerateV6DSW(_DSWGenerator): + """Generates a Workspace file for MSVS 6.0""" + + def PrintWorkspace(self): + """ writes a DSW file """ + name = self.name + dspfile = os.path.relpath(self.dspfiles[0], self.dsw_folder_path) + self.file.write(V6DSWHeader % locals()) + + def Build(self): + try: + self.file = open(self.dswfile,'w') + except IOError, detail: + raise SCons.Errors.InternalError('Unable to open "' + self.dswfile + '" for writing:' + str(detail)) + else: + self.PrintWorkspace() + self.file.close() + + +def GenerateDSP(dspfile, source, env): + """Generates a Project file based on the version of MSVS that is being used""" + + version_num = 6.0 + if 'MSVS_VERSION' in env: + version_num, suite = msvs_parse_version(env['MSVS_VERSION']) + if version_num >= 10.0: + g = _GenerateV10DSP(dspfile, source, env) + g.Build() + elif version_num >= 7.0: + g = _GenerateV7DSP(dspfile, source, env) + g.Build() + else: + g = _GenerateV6DSP(dspfile, source, env) + g.Build() + +def GenerateDSW(dswfile, source, env): + """Generates a Solution/Workspace file based on the version of MSVS that is being used""" + + version_num = 6.0 + if 'MSVS_VERSION' in env: + version_num, suite = msvs_parse_version(env['MSVS_VERSION']) + if version_num >= 7.0: + g = _GenerateV7DSW(dswfile, source, env) + g.Build() + else: + g = _GenerateV6DSW(dswfile, source, env) + g.Build() + + +############################################################################## +# Above here are the classes and functions for generation of +# DSP/DSW/SLN/VCPROJ files. +############################################################################## + +def GetMSVSProjectSuffix(target, source, env, for_signature): + return env['MSVS']['PROJECTSUFFIX'] + +def GetMSVSSolutionSuffix(target, source, env, for_signature): + return env['MSVS']['SOLUTIONSUFFIX'] + +def GenerateProject(target, source, env): + # generate the dsp file, according to the version of MSVS. + builddspfile = target[0] + dspfile = builddspfile.srcnode() + + # this detects whether or not we're using a VariantDir + if not dspfile is builddspfile: + try: + bdsp = open(str(builddspfile), "w+") + except IOError, detail: + print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n' + raise + + bdsp.write("This is just a placeholder file.\nThe real project file is here:\n%s\n" % dspfile.get_abspath()) + + GenerateDSP(dspfile, source, env) + + if env.get('auto_build_solution', 1): + builddswfile = target[1] + dswfile = builddswfile.srcnode() + + if not dswfile is builddswfile: + + try: + bdsw = open(str(builddswfile), "w+") + except IOError, detail: + print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n' + raise + + bdsw.write("This is just a placeholder file.\nThe real workspace file is here:\n%s\n" % dswfile.get_abspath()) + + GenerateDSW(dswfile, source, env) + +def GenerateSolution(target, source, env): + GenerateDSW(target[0], source, env) + +def projectEmitter(target, source, env): + """Sets up the DSP dependencies.""" + + # todo: Not sure what sets source to what user has passed as target, + # but this is what happens. When that is fixed, we also won't have + # to make the user always append env['MSVSPROJECTSUFFIX'] to target. + if source[0] == target[0]: + source = [] + + # make sure the suffix is correct for the version of MSVS we're running. + (base, suff) = SCons.Util.splitext(str(target[0])) + suff = env.subst('$MSVSPROJECTSUFFIX') + target[0] = base + suff + + if not source: + source = 'prj_inputs:' + source = source + env.subst('$MSVSSCONSCOM', 1) + source = source + env.subst('$MSVSENCODING', 1) + + # Project file depends on CPPDEFINES and CPPPATH + preprocdefs = xmlify(';'.join(processDefines(env.get('CPPDEFINES', [])))) + includepath_Dirs = processIncludes(env.get('CPPPATH', []), env, None, None) + includepath = xmlify(';'.join([str(x) for x in includepath_Dirs])) + source = source + "; ppdefs:%s incpath:%s"%(preprocdefs, includepath) + + if 'buildtarget' in env and env['buildtarget'] != None: + if SCons.Util.is_String(env['buildtarget']): + source = source + ' "%s"' % env['buildtarget'] + elif SCons.Util.is_List(env['buildtarget']): + for bt in env['buildtarget']: + if SCons.Util.is_String(bt): + source = source + ' "%s"' % bt + else: + try: source = source + ' "%s"' % bt.get_abspath() + except AttributeError: raise SCons.Errors.InternalError("buildtarget can be a string, a node, a list of strings or nodes, or None") + else: + try: source = source + ' "%s"' % env['buildtarget'].get_abspath() + except AttributeError: raise SCons.Errors.InternalError("buildtarget can be a string, a node, a list of strings or nodes, or None") + + if 'outdir' in env and env['outdir'] != None: + if SCons.Util.is_String(env['outdir']): + source = source + ' "%s"' % env['outdir'] + elif SCons.Util.is_List(env['outdir']): + for s in env['outdir']: + if SCons.Util.is_String(s): + source = source + ' "%s"' % s + else: + try: source = source + ' "%s"' % s.get_abspath() + except AttributeError: raise SCons.Errors.InternalError("outdir can be a string, a node, a list of strings or nodes, or None") + else: + try: source = source + ' "%s"' % env['outdir'].get_abspath() + except AttributeError: raise SCons.Errors.InternalError("outdir can be a string, a node, a list of strings or nodes, or None") + + if 'name' in env: + if SCons.Util.is_String(env['name']): + source = source + ' "%s"' % env['name'] + else: + raise SCons.Errors.InternalError("name must be a string") + + if 'variant' in env: + if SCons.Util.is_String(env['variant']): + source = source + ' "%s"' % env['variant'] + elif SCons.Util.is_List(env['variant']): + for variant in env['variant']: + if SCons.Util.is_String(variant): + source = source + ' "%s"' % variant + else: + raise SCons.Errors.InternalError("name must be a string or a list of strings") + else: + raise SCons.Errors.InternalError("variant must be a string or a list of strings") + else: + raise SCons.Errors.InternalError("variant must be specified") + + for s in _DSPGenerator.srcargs: + if s in env: + if SCons.Util.is_String(env[s]): + source = source + ' "%s' % env[s] + elif SCons.Util.is_List(env[s]): + for t in env[s]: + if SCons.Util.is_String(t): + source = source + ' "%s"' % t + else: + raise SCons.Errors.InternalError(s + " must be a string or a list of strings") + else: + raise SCons.Errors.InternalError(s + " must be a string or a list of strings") + + source = source + ' "%s"' % str(target[0]) + source = [SCons.Node.Python.Value(source)] + + targetlist = [target[0]] + sourcelist = source + + if env.get('auto_build_solution', 1): + env['projects'] = [env.File(t).srcnode() for t in targetlist] + t, s = solutionEmitter(target, target, env) + targetlist = targetlist + t + + # Beginning with Visual Studio 2010 for each project file (.vcxproj) we have additional file (.vcxproj.filters) + if float(env['MSVS_VERSION']) >= 10.0: + targetlist.append(targetlist[0] + '.filters') + + return (targetlist, sourcelist) + +def solutionEmitter(target, source, env): + """Sets up the DSW dependencies.""" + + # todo: Not sure what sets source to what user has passed as target, + # but this is what happens. When that is fixed, we also won't have + # to make the user always append env['MSVSSOLUTIONSUFFIX'] to target. + if source[0] == target[0]: + source = [] + + # make sure the suffix is correct for the version of MSVS we're running. + (base, suff) = SCons.Util.splitext(str(target[0])) + suff = env.subst('$MSVSSOLUTIONSUFFIX') + target[0] = base + suff + + if not source: + source = 'sln_inputs:' + + if 'name' in env: + if SCons.Util.is_String(env['name']): + source = source + ' "%s"' % env['name'] + else: + raise SCons.Errors.InternalError("name must be a string") + + if 'variant' in env: + if SCons.Util.is_String(env['variant']): + source = source + ' "%s"' % env['variant'] + elif SCons.Util.is_List(env['variant']): + for variant in env['variant']: + if SCons.Util.is_String(variant): + source = source + ' "%s"' % variant + else: + raise SCons.Errors.InternalError("name must be a string or a list of strings") + else: + raise SCons.Errors.InternalError("variant must be a string or a list of strings") + else: + raise SCons.Errors.InternalError("variant must be specified") + + if 'slnguid' in env: + if SCons.Util.is_String(env['slnguid']): + source = source + ' "%s"' % env['slnguid'] + else: + raise SCons.Errors.InternalError("slnguid must be a string") + + if 'projects' in env: + if SCons.Util.is_String(env['projects']): + source = source + ' "%s"' % env['projects'] + elif SCons.Util.is_List(env['projects']): + for t in env['projects']: + if SCons.Util.is_String(t): + source = source + ' "%s"' % t + + source = source + ' "%s"' % str(target[0]) + source = [SCons.Node.Python.Value(source)] + + return ([target[0]], source) + +projectAction = SCons.Action.Action(GenerateProject, None) + +solutionAction = SCons.Action.Action(GenerateSolution, None) + +projectBuilder = SCons.Builder.Builder(action = '$MSVSPROJECTCOM', + suffix = '$MSVSPROJECTSUFFIX', + emitter = projectEmitter) + +solutionBuilder = SCons.Builder.Builder(action = '$MSVSSOLUTIONCOM', + suffix = '$MSVSSOLUTIONSUFFIX', + emitter = solutionEmitter) + +default_MSVS_SConscript = None + +def generate(env): + """Add Builders and construction variables for Microsoft Visual + Studio project files to an Environment.""" + try: + env['BUILDERS']['MSVSProject'] + except KeyError: + env['BUILDERS']['MSVSProject'] = projectBuilder + + try: + env['BUILDERS']['MSVSSolution'] + except KeyError: + env['BUILDERS']['MSVSSolution'] = solutionBuilder + + env['MSVSPROJECTCOM'] = projectAction + env['MSVSSOLUTIONCOM'] = solutionAction + + if SCons.Script.call_stack: + # XXX Need to find a way to abstract this; the build engine + # shouldn't depend on anything in SCons.Script. + env['MSVSSCONSCRIPT'] = SCons.Script.call_stack[0].sconscript + else: + global default_MSVS_SConscript + if default_MSVS_SConscript is None: + default_MSVS_SConscript = env.File('SConstruct') + env['MSVSSCONSCRIPT'] = default_MSVS_SConscript + + env['MSVSSCONS'] = '"%s" -c "%s"' % (python_executable, getExecScriptMain(env)) + env['MSVSSCONSFLAGS'] = '-C "${MSVSSCONSCRIPT.dir.abspath}" -f ${MSVSSCONSCRIPT.name}' + env['MSVSSCONSCOM'] = '$MSVSSCONS $MSVSSCONSFLAGS' + env['MSVSBUILDCOM'] = '$MSVSSCONSCOM "$MSVSBUILDTARGET"' + env['MSVSREBUILDCOM'] = '$MSVSSCONSCOM "$MSVSBUILDTARGET"' + env['MSVSCLEANCOM'] = '$MSVSSCONSCOM -c "$MSVSBUILDTARGET"' + + # Set-up ms tools paths for default version + msvc_setup_env_once(env) + + if 'MSVS_VERSION' in env: + version_num, suite = msvs_parse_version(env['MSVS_VERSION']) + else: + (version_num, suite) = (7.0, None) # guess at a default + if 'MSVS' not in env: + env['MSVS'] = {} + if (version_num < 7.0): + env['MSVS']['PROJECTSUFFIX'] = '.dsp' + env['MSVS']['SOLUTIONSUFFIX'] = '.dsw' + elif (version_num < 10.0): + env['MSVS']['PROJECTSUFFIX'] = '.vcproj' + env['MSVS']['SOLUTIONSUFFIX'] = '.sln' + else: + env['MSVS']['PROJECTSUFFIX'] = '.vcxproj' + env['MSVS']['SOLUTIONSUFFIX'] = '.sln' + + if (version_num >= 10.0): + env['MSVSENCODING'] = 'utf-8' + else: + env['MSVSENCODING'] = 'Windows-1252' + + env['GET_MSVSPROJECTSUFFIX'] = GetMSVSProjectSuffix + env['GET_MSVSSOLUTIONSUFFIX'] = GetMSVSSolutionSuffix + env['MSVSPROJECTSUFFIX'] = '${GET_MSVSPROJECTSUFFIX}' + env['MSVSSOLUTIONSUFFIX'] = '${GET_MSVSSOLUTIONSUFFIX}' + env['SCONS_HOME'] = os.environ.get('SCONS_HOME') + +def exists(env): + return msvc_exists() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/mwcc.py b/scons/scons-local-2.3.0/SCons/Tool/mwcc.py new file mode 100644 index 000000000..48433012f --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/mwcc.py @@ -0,0 +1,207 @@ +"""SCons.Tool.mwcc + +Tool-specific initialization for the Metrowerks CodeWarrior compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/mwcc.py 2013/03/03 09:48:35 garyo" + +import os +import os.path + +import SCons.Util + +def set_vars(env): + """Set MWCW_VERSION, MWCW_VERSIONS, and some codewarrior environment vars + + MWCW_VERSIONS is set to a list of objects representing installed versions + + MWCW_VERSION is set to the version object that will be used for building. + MWCW_VERSION can be set to a string during Environment + construction to influence which version is chosen, otherwise + the latest one from MWCW_VERSIONS is used. + + Returns true if at least one version is found, false otherwise + """ + desired = env.get('MWCW_VERSION', '') + + # return right away if the variables are already set + if isinstance(desired, MWVersion): + return 1 + elif desired is None: + return 0 + + versions = find_versions() + version = None + + if desired: + for v in versions: + if str(v) == desired: + version = v + elif versions: + version = versions[-1] + + env['MWCW_VERSIONS'] = versions + env['MWCW_VERSION'] = version + + if version is None: + return 0 + + env.PrependENVPath('PATH', version.clpath) + env.PrependENVPath('PATH', version.dllpath) + ENV = env['ENV'] + ENV['CWFolder'] = version.path + ENV['LM_LICENSE_FILE'] = version.license + plus = lambda x: '+%s' % x + ENV['MWCIncludes'] = os.pathsep.join(map(plus, version.includes)) + ENV['MWLibraries'] = os.pathsep.join(map(plus, version.libs)) + return 1 + + +def find_versions(): + """Return a list of MWVersion objects representing installed versions""" + versions = [] + + ### This function finds CodeWarrior by reading from the registry on + ### Windows. Some other method needs to be implemented for other + ### platforms, maybe something that calls env.WhereIs('mwcc') + + if SCons.Util.can_read_reg: + try: + HLM = SCons.Util.HKEY_LOCAL_MACHINE + product = 'SOFTWARE\\Metrowerks\\CodeWarrior\\Product Versions' + product_key = SCons.Util.RegOpenKeyEx(HLM, product) + + i = 0 + while True: + name = product + '\\' + SCons.Util.RegEnumKey(product_key, i) + name_key = SCons.Util.RegOpenKeyEx(HLM, name) + + try: + version = SCons.Util.RegQueryValueEx(name_key, 'VERSION') + path = SCons.Util.RegQueryValueEx(name_key, 'PATH') + mwv = MWVersion(version[0], path[0], 'Win32-X86') + versions.append(mwv) + except SCons.Util.RegError: + pass + + i = i + 1 + + except SCons.Util.RegError: + pass + + return versions + + +class MWVersion(object): + def __init__(self, version, path, platform): + self.version = version + self.path = path + self.platform = platform + self.clpath = os.path.join(path, 'Other Metrowerks Tools', + 'Command Line Tools') + self.dllpath = os.path.join(path, 'Bin') + + # The Metrowerks tools don't store any configuration data so they + # are totally dumb when it comes to locating standard headers, + # libraries, and other files, expecting all the information + # to be handed to them in environment variables. The members set + # below control what information scons injects into the environment + + ### The paths below give a normal build environment in CodeWarrior for + ### Windows, other versions of CodeWarrior might need different paths. + + msl = os.path.join(path, 'MSL') + support = os.path.join(path, '%s Support' % platform) + + self.license = os.path.join(path, 'license.dat') + self.includes = [msl, support] + self.libs = [msl, support] + + def __str__(self): + return self.version + + +CSuffixes = ['.c', '.C'] +CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++'] + + +def generate(env): + """Add Builders and construction variables for the mwcc to an Environment.""" + import SCons.Defaults + import SCons.Tool + + set_vars(env) + + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + for suffix in CSuffixes: + static_obj.add_action(suffix, SCons.Defaults.CAction) + shared_obj.add_action(suffix, SCons.Defaults.ShCAction) + + for suffix in CXXSuffixes: + static_obj.add_action(suffix, SCons.Defaults.CXXAction) + shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction) + + env['CCCOMFLAGS'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -nolink -o $TARGET $SOURCES' + + env['CC'] = 'mwcc' + env['CCCOM'] = '$CC $CFLAGS $CCFLAGS $CCCOMFLAGS' + + env['CXX'] = 'mwcc' + env['CXXCOM'] = '$CXX $CXXFLAGS $CCCOMFLAGS' + + env['SHCC'] = '$CC' + env['SHCCFLAGS'] = '$CCFLAGS' + env['SHCFLAGS'] = '$CFLAGS' + env['SHCCCOM'] = '$SHCC $SHCFLAGS $SHCCFLAGS $CCCOMFLAGS' + + env['SHCXX'] = '$CXX' + env['SHCXXFLAGS'] = '$CXXFLAGS' + env['SHCXXCOM'] = '$SHCXX $SHCXXFLAGS $CCCOMFLAGS' + + env['CFILESUFFIX'] = '.c' + env['CXXFILESUFFIX'] = '.cpp' + env['CPPDEFPREFIX'] = '-D' + env['CPPDEFSUFFIX'] = '' + env['INCPREFIX'] = '-I' + env['INCSUFFIX'] = '' + + #env['PCH'] = ? + #env['PCHSTOP'] = ? + + +def exists(env): + return set_vars(env) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/mwld.py b/scons/scons-local-2.3.0/SCons/Tool/mwld.py new file mode 100644 index 000000000..ff875a57d --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/mwld.py @@ -0,0 +1,107 @@ +"""SCons.Tool.mwld + +Tool-specific initialization for the Metrowerks CodeWarrior linker. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/mwld.py 2013/03/03 09:48:35 garyo" + +import SCons.Tool + + +def generate(env): + """Add Builders and construction variables for lib to an Environment.""" + SCons.Tool.createStaticLibBuilder(env) + SCons.Tool.createSharedLibBuilder(env) + SCons.Tool.createProgBuilder(env) + + env['AR'] = 'mwld' + env['ARCOM'] = '$AR $ARFLAGS -library -o $TARGET $SOURCES' + + env['LIBDIRPREFIX'] = '-L' + env['LIBDIRSUFFIX'] = '' + env['LIBLINKPREFIX'] = '-l' + env['LIBLINKSUFFIX'] = '.lib' + + env['LINK'] = 'mwld' + env['LINKCOM'] = '$LINK $LINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' + + env['SHLINK'] = '$LINK' + env['SHLINKFLAGS'] = '$LINKFLAGS' + env['SHLINKCOM'] = shlib_action + env['SHLIBEMITTER']= shlib_emitter + + +def exists(env): + import SCons.Tool.mwcc + return SCons.Tool.mwcc.set_vars(env) + + +def shlib_generator(target, source, env, for_signature): + cmd = ['$SHLINK', '$SHLINKFLAGS', '-shared'] + + no_import_lib = env.get('no_import_lib', 0) + if no_import_lib: cmd.extend('-noimplib') + + dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') + if dll: cmd.extend(['-o', dll]) + + implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX') + if implib: cmd.extend(['-implib', implib.get_string(for_signature)]) + + cmd.extend(['$SOURCES', '$_LIBDIRFLAGS', '$_LIBFLAGS']) + + return [cmd] + + +def shlib_emitter(target, source, env): + dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX') + no_import_lib = env.get('no_import_lib', 0) + + if not dll: + raise SCons.Errors.UserError("A shared library should have exactly one target with the suffix: %s" % env.subst("$SHLIBSUFFIX")) + + if not no_import_lib and \ + not env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX'): + + # Append an import library to the list of targets. + target.append(env.ReplaceIxes(dll, + 'SHLIBPREFIX', 'SHLIBSUFFIX', + 'LIBPREFIX', 'LIBSUFFIX')) + + return target, source + + +shlib_action = SCons.Action.Action(shlib_generator, generator=1) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/nasm.py b/scons/scons-local-2.3.0/SCons/Tool/nasm.py new file mode 100644 index 000000000..d754a2afb --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/nasm.py @@ -0,0 +1,72 @@ +"""SCons.Tool.nasm + +Tool-specific initialization for nasm, the famous Netwide Assembler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/nasm.py 2013/03/03 09:48:35 garyo" + +import SCons.Defaults +import SCons.Tool +import SCons.Util + +ASSuffixes = ['.s', '.asm', '.ASM'] +ASPPSuffixes = ['.spp', '.SPP', '.sx'] +if SCons.Util.case_sensitive_suffixes('.s', '.S'): + ASPPSuffixes.extend(['.S']) +else: + ASSuffixes.extend(['.S']) + +def generate(env): + """Add Builders and construction variables for nasm to an Environment.""" + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + for suffix in ASSuffixes: + static_obj.add_action(suffix, SCons.Defaults.ASAction) + static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) + + for suffix in ASPPSuffixes: + static_obj.add_action(suffix, SCons.Defaults.ASPPAction) + static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter) + + env['AS'] = 'nasm' + env['ASFLAGS'] = SCons.Util.CLVar('') + env['ASPPFLAGS'] = '$ASFLAGS' + env['ASCOM'] = '$AS $ASFLAGS -o $TARGET $SOURCES' + env['ASPPCOM'] = '$CC $ASPPFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES' + +def exists(env): + return env.Detect('nasm') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/packaging/__init__.py b/scons/scons-local-2.3.0/SCons/Tool/packaging/__init__.py new file mode 100644 index 000000000..b0a7549e2 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/packaging/__init__.py @@ -0,0 +1,312 @@ +"""SCons.Tool.Packaging + +SCons Packaging Tool. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Tool/packaging/__init__.py 2013/03/03 09:48:35 garyo" + +import SCons.Environment +from SCons.Variables import * +from SCons.Errors import * +from SCons.Util import is_List, make_path_relative +from SCons.Warnings import warn, Warning + +import os, imp +import SCons.Defaults + +__all__ = [ 'src_targz', 'src_tarbz2', 'src_zip', 'tarbz2', 'targz', 'zip', 'rpm', 'msi', 'ipk' ] + +# +# Utility and Builder function +# +def Tag(env, target, source, *more_tags, **kw_tags): + """ Tag a file with the given arguments, just sets the accordingly named + attribute on the file object. + + TODO: FIXME + """ + if not target: + target=source + first_tag=None + else: + first_tag=source + + if first_tag: + kw_tags[first_tag[0]] = '' + + if len(kw_tags) == 0 and len(more_tags) == 0: + raise UserError("No tags given.") + + # XXX: sanity checks + for x in more_tags: + kw_tags[x] = '' + + if not SCons.Util.is_List(target): + target=[target] + else: + # hmm, sometimes the target list, is a list of a list + # make sure it is flattened prior to processing. + # TODO: perhaps some bug ?!? + target=env.Flatten(target) + + for t in target: + for (k,v) in kw_tags.items(): + # all file tags have to start with PACKAGING_, so we can later + # differentiate between "normal" object attributes and the + # packaging attributes. As the user should not be bothered with + # that, the prefix will be added here if missing. + #if not k.startswith('PACKAGING_'): + if k[:10] != 'PACKAGING_': + k='PACKAGING_'+k + setattr(t, k, v) + +def Package(env, target=None, source=None, **kw): + """ Entry point for the package tool. + """ + # check if we need to find the source files ourself + if not source: + source = env.FindInstalledFiles() + + if len(source)==0: + raise UserError("No source for Package() given") + + # decide which types of packages shall be built. Can be defined through + # four mechanisms: command line argument, keyword argument, + # environment argument and default selection( zip or tar.gz ) in that + # order. + try: kw['PACKAGETYPE']=env['PACKAGETYPE'] + except KeyError: pass + + if not kw.get('PACKAGETYPE'): + from SCons.Script import GetOption + kw['PACKAGETYPE'] = GetOption('package_type') + + if kw['PACKAGETYPE'] == None: + if 'Tar' in env['BUILDERS']: + kw['PACKAGETYPE']='targz' + elif 'Zip' in env['BUILDERS']: + kw['PACKAGETYPE']='zip' + else: + raise UserError("No type for Package() given") + + PACKAGETYPE=kw['PACKAGETYPE'] + if not is_List(PACKAGETYPE): + PACKAGETYPE=PACKAGETYPE.split(',') + + # load the needed packagers. + def load_packager(type): + try: + file,path,desc=imp.find_module(type, __path__) + return imp.load_module(type, file, path, desc) + except ImportError, e: + raise EnvironmentError("packager %s not available: %s"%(type,str(e))) + + packagers=list(map(load_packager, PACKAGETYPE)) + + # set up targets and the PACKAGEROOT + try: + # fill up the target list with a default target name until the PACKAGETYPE + # list is of the same size as the target list. + if not target: target = [] + + size_diff = len(PACKAGETYPE)-len(target) + default_name = "%(NAME)s-%(VERSION)s" + + if size_diff>0: + default_target = default_name%kw + target.extend( [default_target]*size_diff ) + + if 'PACKAGEROOT' not in kw: + kw['PACKAGEROOT'] = default_name%kw + + except KeyError, e: + raise SCons.Errors.UserError( "Missing Packagetag '%s'"%e.args[0] ) + + # setup the source files + source=env.arg2nodes(source, env.fs.Entry) + + # call the packager to setup the dependencies. + targets=[] + try: + for packager in packagers: + t=[target.pop(0)] + t=packager.package(env,t,source, **kw) + targets.extend(t) + + assert( len(target) == 0 ) + + except KeyError, e: + raise SCons.Errors.UserError( "Missing Packagetag '%s' for %s packager"\ + % (e.args[0],packager.__name__) ) + except TypeError, e: + # this exception means that a needed argument for the packager is + # missing. As our packagers get their "tags" as named function + # arguments we need to find out which one is missing. + from inspect import getargspec + args,varargs,varkw,defaults=getargspec(packager.package) + if defaults!=None: + args=args[:-len(defaults)] # throw away arguments with default values + args.remove('env') + args.remove('target') + args.remove('source') + # now remove any args for which we have a value in kw. + args=[x for x in args if x not in kw] + + if len(args)==0: + raise # must be a different error, so reraise + elif len(args)==1: + raise SCons.Errors.UserError( "Missing Packagetag '%s' for %s packager"\ + % (args[0],packager.__name__) ) + else: + raise SCons.Errors.UserError( "Missing Packagetags '%s' for %s packager"\ + % (", ".join(args),packager.__name__) ) + + target=env.arg2nodes(target, env.fs.Entry) + targets.extend(env.Alias( 'package', targets )) + return targets + +# +# SCons tool initialization functions +# + +added = None + +def generate(env): + from SCons.Script import AddOption + global added + if not added: + added = 1 + AddOption('--package-type', + dest='package_type', + default=None, + type="string", + action="store", + help='The type of package to create.') + + try: + env['BUILDERS']['Package'] + env['BUILDERS']['Tag'] + except KeyError: + env['BUILDERS']['Package'] = Package + env['BUILDERS']['Tag'] = Tag + +def exists(env): + return 1 + +# XXX +def options(opts): + opts.AddVariables( + EnumVariable( 'PACKAGETYPE', + 'the type of package to create.', + None, allowed_values=list(map( str, __all__ )), + ignorecase=2 + ) + ) + +# +# Internal utility functions +# + +def copy_attr(f1, f2): + """ copies the special packaging file attributes from f1 to f2. + """ + #pattrs = [x for x in dir(f1) if not hasattr(f2, x) and\ + # x.startswith('PACKAGING_')] + copyit = lambda x: not hasattr(f2, x) and x[:10] == 'PACKAGING_' + pattrs = list(filter(copyit, dir(f1))) + for attr in pattrs: + setattr(f2, attr, getattr(f1, attr)) +def putintopackageroot(target, source, env, pkgroot, honor_install_location=1): + """ Uses the CopyAs builder to copy all source files to the directory given + in pkgroot. + + If honor_install_location is set and the copied source file has an + PACKAGING_INSTALL_LOCATION attribute, the PACKAGING_INSTALL_LOCATION is + used as the new name of the source file under pkgroot. + + The source file will not be copied if it is already under the the pkgroot + directory. + + All attributes of the source file will be copied to the new file. + """ + # make sure the packageroot is a Dir object. + if SCons.Util.is_String(pkgroot): pkgroot=env.Dir(pkgroot) + if not SCons.Util.is_List(source): source=[source] + + new_source = [] + for file in source: + if SCons.Util.is_String(file): file = env.File(file) + + if file.is_under(pkgroot): + new_source.append(file) + else: + if hasattr(file, 'PACKAGING_INSTALL_LOCATION') and\ + honor_install_location: + new_name=make_path_relative(file.PACKAGING_INSTALL_LOCATION) + else: + new_name=make_path_relative(file.get_path()) + + new_file=pkgroot.File(new_name) + new_file=env.CopyAs(new_file, file)[0] + copy_attr(file, new_file) + new_source.append(new_file) + + return (target, new_source) + +def stripinstallbuilder(target, source, env): + """ strips the install builder action from the source list and stores + the final installation location as the "PACKAGING_INSTALL_LOCATION" of + the source of the source file. This effectively removes the final installed + files from the source list while remembering the installation location. + + It also warns about files which have no install builder attached. + """ + def has_no_install_location(file): + return not (file.has_builder() and\ + hasattr(file.builder, 'name') and\ + (file.builder.name=="InstallBuilder" or\ + file.builder.name=="InstallAsBuilder")) + + if len(list(filter(has_no_install_location, source))): + warn(Warning, "there are files to package which have no\ + InstallBuilder attached, this might lead to irreproducible packages") + + n_source=[] + for s in source: + if has_no_install_location(s): + n_source.append(s) + else: + for ss in s.sources: + n_source.append(ss) + copy_attr(s, ss) + setattr(ss, 'PACKAGING_INSTALL_LOCATION', s.get_path()) + + return (target, n_source) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/packaging/ipk.py b/scons/scons-local-2.3.0/SCons/Tool/packaging/ipk.py new file mode 100644 index 000000000..77c6420d9 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/packaging/ipk.py @@ -0,0 +1,185 @@ +"""SCons.Tool.Packaging.ipk +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/packaging/ipk.py 2013/03/03 09:48:35 garyo" + +import SCons.Builder +import SCons.Node.FS +import os + +from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot + +def package(env, target, source, PACKAGEROOT, NAME, VERSION, DESCRIPTION, + SUMMARY, X_IPK_PRIORITY, X_IPK_SECTION, SOURCE_URL, + X_IPK_MAINTAINER, X_IPK_DEPENDS, **kw): + """ this function prepares the packageroot directory for packaging with the + ipkg builder. + """ + SCons.Tool.Tool('ipkg').generate(env) + + # setup the Ipkg builder + bld = env['BUILDERS']['Ipkg'] + target, source = stripinstallbuilder(target, source, env) + target, source = putintopackageroot(target, source, env, PACKAGEROOT) + + # This should be overridable from the construction environment, + # which it is by using ARCHITECTURE=. + # Guessing based on what os.uname() returns at least allows it + # to work for both i386 and x86_64 Linux systems. + archmap = { + 'i686' : 'i386', + 'i586' : 'i386', + 'i486' : 'i386', + } + + buildarchitecture = os.uname()[4] + buildarchitecture = archmap.get(buildarchitecture, buildarchitecture) + + if 'ARCHITECTURE' in kw: + buildarchitecture = kw['ARCHITECTURE'] + + # setup the kw to contain the mandatory arguments to this fucntion. + # do this before calling any builder or setup function + loc=locals() + del loc['kw'] + kw.update(loc) + del kw['source'], kw['target'], kw['env'] + + # generate the specfile + specfile = gen_ipk_dir(PACKAGEROOT, source, env, kw) + + # override the default target. + if str(target[0])=="%s-%s"%(NAME, VERSION): + target=[ "%s_%s_%s.ipk"%(NAME, VERSION, buildarchitecture) ] + + # now apply the Ipkg builder + return bld(env, target, specfile, **kw) + +def gen_ipk_dir(proot, source, env, kw): + # make sure the packageroot is a Dir object. + if SCons.Util.is_String(proot): proot=env.Dir(proot) + + # create the specfile builder + s_bld=SCons.Builder.Builder( + action = build_specfiles, + ) + + # create the specfile targets + spec_target=[] + control=proot.Dir('CONTROL') + spec_target.append(control.File('control')) + spec_target.append(control.File('conffiles')) + spec_target.append(control.File('postrm')) + spec_target.append(control.File('prerm')) + spec_target.append(control.File('postinst')) + spec_target.append(control.File('preinst')) + + # apply the builder to the specfile targets + s_bld(env, spec_target, source, **kw) + + # the packageroot directory does now contain the specfiles. + return proot + +def build_specfiles(source, target, env): + """ filter the targets for the needed files and use the variables in env + to create the specfile. + """ + # + # At first we care for the CONTROL/control file, which is the main file for ipk. + # + # For this we need to open multiple files in random order, so we store into + # a dict so they can be easily accessed. + # + # + opened_files={} + def open_file(needle, haystack): + try: + return opened_files[needle] + except KeyError: + file=filter(lambda x: x.get_path().rfind(needle)!=-1, haystack)[0] + opened_files[needle]=open(file.abspath, 'w') + return opened_files[needle] + + control_file=open_file('control', target) + + if 'X_IPK_DESCRIPTION' not in env: + env['X_IPK_DESCRIPTION']="%s\n %s"%(env['SUMMARY'], + env['DESCRIPTION'].replace('\n', '\n ')) + + + content = """ +Package: $NAME +Version: $VERSION +Priority: $X_IPK_PRIORITY +Section: $X_IPK_SECTION +Source: $SOURCE_URL +Architecture: $ARCHITECTURE +Maintainer: $X_IPK_MAINTAINER +Depends: $X_IPK_DEPENDS +Description: $X_IPK_DESCRIPTION +""" + + control_file.write(env.subst(content)) + + # + # now handle the various other files, which purpose it is to set post-, + # pre-scripts and mark files as config files. + # + # We do so by filtering the source files for files which are marked with + # the "config" tag and afterwards we do the same for x_ipk_postrm, + # x_ipk_prerm, x_ipk_postinst and x_ipk_preinst tags. + # + # The first one will write the name of the file into the file + # CONTROL/configfiles, the latter add the content of the x_ipk_* variable + # into the same named file. + # + for f in [x for x in source if 'PACKAGING_CONFIG' in dir(x)]: + config=open_file('conffiles') + config.write(f.PACKAGING_INSTALL_LOCATION) + config.write('\n') + + for str in 'POSTRM PRERM POSTINST PREINST'.split(): + name="PACKAGING_X_IPK_%s"%str + for f in [x for x in source if name in dir(x)]: + file=open_file(name) + file.write(env[str]) + + # + # close all opened files + for f in opened_files.values(): + f.close() + + # call a user specified function + if 'CHANGE_SPECFILE' in env: + content += env['CHANGE_SPECFILE'](target) + + return 0 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/packaging/msi.py b/scons/scons-local-2.3.0/SCons/Tool/packaging/msi.py new file mode 100644 index 000000000..26eb63041 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/packaging/msi.py @@ -0,0 +1,527 @@ +"""SCons.Tool.packaging.msi + +The msi packager. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Tool/packaging/msi.py 2013/03/03 09:48:35 garyo" + +import os +import SCons +from SCons.Action import Action +from SCons.Builder import Builder + +from xml.dom.minidom import * +from xml.sax.saxutils import escape + +from SCons.Tool.packaging import stripinstallbuilder + +# +# Utility functions +# +def convert_to_id(s, id_set): + """ Some parts of .wxs need an Id attribute (for example: The File and + Directory directives. The charset is limited to A-Z, a-z, digits, + underscores, periods. Each Id must begin with a letter or with a + underscore. Google for "CNDL0015" for information about this. + + Requirements: + * the string created must only contain chars from the target charset. + * the string created must have a minimal editing distance from the + original string. + * the string created must be unique for the whole .wxs file. + + Observation: + * There are 62 chars in the charset. + + Idea: + * filter out forbidden characters. Check for a collision with the help + of the id_set. Add the number of the number of the collision at the + end of the created string. Furthermore care for a correct start of + the string. + """ + charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxyz0123456789_.' + if s[0] in '0123456789.': + s += '_'+s + id = [c for c in s if c in charset] + + # did we already generate an id for this file? + try: + return id_set[id][s] + except KeyError: + # no we did not so initialize with the id + if id not in id_set: id_set[id] = { s : id } + # there is a collision, generate an id which is unique by appending + # the collision number + else: id_set[id][s] = id + str(len(id_set[id])) + + return id_set[id][s] + +def is_dos_short_file_name(file): + """ examine if the given file is in the 8.3 form. + """ + fname, ext = os.path.splitext(file) + proper_ext = len(ext) == 0 or (2 <= len(ext) <= 4) # the ext contains the dot + proper_fname = file.isupper() and len(fname) <= 8 + + return proper_ext and proper_fname + +def gen_dos_short_file_name(file, filename_set): + """ see http://support.microsoft.com/default.aspx?scid=kb;en-us;Q142982 + + These are no complete 8.3 dos short names. The ~ char is missing and + replaced with one character from the filename. WiX warns about such + filenames, since a collision might occur. Google for "CNDL1014" for + more information. + """ + # guard this to not confuse the generation + if is_dos_short_file_name(file): + return file + + fname, ext = os.path.splitext(file) # ext contains the dot + + # first try if it suffices to convert to upper + file = file.upper() + if is_dos_short_file_name(file): + return file + + # strip forbidden characters. + forbidden = '."/[]:;=, ' + fname = [c for c in fname if c not in forbidden] + + # check if we already generated a filename with the same number: + # thisis1.txt, thisis2.txt etc. + duplicate, num = not None, 1 + while duplicate: + shortname = "%s%s" % (fname[:8-len(str(num))].upper(),\ + str(num)) + if len(ext) >= 2: + shortname = "%s%s" % (shortname, ext[:4].upper()) + + duplicate, num = shortname in filename_set, num+1 + + assert( is_dos_short_file_name(shortname) ), 'shortname is %s, longname is %s' % (shortname, file) + filename_set.append(shortname) + return shortname + +def create_feature_dict(files): + """ X_MSI_FEATURE and doc FileTag's can be used to collect files in a + hierarchy. This function collects the files into this hierarchy. + """ + dict = {} + + def add_to_dict( feature, file ): + if not SCons.Util.is_List( feature ): + feature = [ feature ] + + for f in feature: + if f not in dict: + dict[ f ] = [ file ] + else: + dict[ f ].append( file ) + + for file in files: + if hasattr( file, 'PACKAGING_X_MSI_FEATURE' ): + add_to_dict(file.PACKAGING_X_MSI_FEATURE, file) + elif hasattr( file, 'PACKAGING_DOC' ): + add_to_dict( 'PACKAGING_DOC', file ) + else: + add_to_dict( 'default', file ) + + return dict + +def generate_guids(root): + """ generates globally unique identifiers for parts of the xml which need + them. + + Component tags have a special requirement. Their UUID is only allowed to + change if the list of their contained resources has changed. This allows + for clean removal and proper updates. + + To handle this requirement, the uuid is generated with an md5 hashing the + whole subtree of a xml node. + """ + from hashlib import md5 + + # specify which tags need a guid and in which attribute this should be stored. + needs_id = { 'Product' : 'Id', + 'Package' : 'Id', + 'Component' : 'Guid', + } + + # find all XMl nodes matching the key, retrieve their attribute, hash their + # subtree, convert hash to string and add as a attribute to the xml node. + for (key,value) in needs_id.items(): + node_list = root.getElementsByTagName(key) + attribute = value + for node in node_list: + hash = md5(node.toxml()).hexdigest() + hash_str = '%s-%s-%s-%s-%s' % ( hash[:8], hash[8:12], hash[12:16], hash[16:20], hash[20:] ) + node.attributes[attribute] = hash_str + + + +def string_wxsfile(target, source, env): + return "building WiX file %s"%( target[0].path ) + +def build_wxsfile(target, source, env): + """ compiles a .wxs file from the keywords given in env['msi_spec'] and + by analyzing the tree of source nodes and their tags. + """ + file = open(target[0].abspath, 'w') + + try: + # Create a document with the Wix root tag + doc = Document() + root = doc.createElement( 'Wix' ) + root.attributes['xmlns']='http://schemas.microsoft.com/wix/2003/01/wi' + doc.appendChild( root ) + + filename_set = [] # this is to circumvent duplicates in the shortnames + id_set = {} # this is to circumvent duplicates in the ids + + # Create the content + build_wxsfile_header_section(root, env) + build_wxsfile_file_section(root, source, env['NAME'], env['VERSION'], env['VENDOR'], filename_set, id_set) + generate_guids(root) + build_wxsfile_features_section(root, source, env['NAME'], env['VERSION'], env['SUMMARY'], id_set) + build_wxsfile_default_gui(root) + build_license_file(target[0].get_dir(), env) + + # write the xml to a file + file.write( doc.toprettyxml() ) + + # call a user specified function + if 'CHANGE_SPECFILE' in env: + env['CHANGE_SPECFILE'](target, source) + + except KeyError, e: + raise SCons.Errors.UserError( '"%s" package field for MSI is missing.' % e.args[0] ) + +# +# setup function +# +def create_default_directory_layout(root, NAME, VERSION, VENDOR, filename_set): + """ Create the wix default target directory layout and return the innermost + directory. + + We assume that the XML tree delivered in the root argument already contains + the Product tag. + + Everything is put under the PFiles directory property defined by WiX. + After that a directory with the 'VENDOR' tag is placed and then a + directory with the name of the project and its VERSION. This leads to the + following TARGET Directory Layout: + C:\\\\ + Example: C:\Programme\Company\Product-1.2\ + """ + doc = Document() + d1 = doc.createElement( 'Directory' ) + d1.attributes['Id'] = 'TARGETDIR' + d1.attributes['Name'] = 'SourceDir' + + d2 = doc.createElement( 'Directory' ) + d2.attributes['Id'] = 'ProgramFilesFolder' + d2.attributes['Name'] = 'PFiles' + + d3 = doc.createElement( 'Directory' ) + d3.attributes['Id'] = 'VENDOR_folder' + d3.attributes['Name'] = escape( gen_dos_short_file_name( VENDOR, filename_set ) ) + d3.attributes['LongName'] = escape( VENDOR ) + + d4 = doc.createElement( 'Directory' ) + project_folder = "%s-%s" % ( NAME, VERSION ) + d4.attributes['Id'] = 'MY_DEFAULT_FOLDER' + d4.attributes['Name'] = escape( gen_dos_short_file_name( project_folder, filename_set ) ) + d4.attributes['LongName'] = escape( project_folder ) + + d1.childNodes.append( d2 ) + d2.childNodes.append( d3 ) + d3.childNodes.append( d4 ) + + root.getElementsByTagName('Product')[0].childNodes.append( d1 ) + + return d4 + +# +# mandatory and optional file tags +# +def build_wxsfile_file_section(root, files, NAME, VERSION, VENDOR, filename_set, id_set): + """ builds the Component sections of the wxs file with their included files. + + Files need to be specified in 8.3 format and in the long name format, long + filenames will be converted automatically. + + Features are specficied with the 'X_MSI_FEATURE' or 'DOC' FileTag. + """ + root = create_default_directory_layout( root, NAME, VERSION, VENDOR, filename_set ) + components = create_feature_dict( files ) + factory = Document() + + def get_directory( node, dir ): + """ returns the node under the given node representing the directory. + + Returns the component node if dir is None or empty. + """ + if dir == '' or not dir: + return node + + Directory = node + dir_parts = dir.split(os.path.sep) + + # to make sure that our directory ids are unique, the parent folders are + # consecutively added to upper_dir + upper_dir = '' + + # walk down the xml tree finding parts of the directory + dir_parts = [d for d in dir_parts if d != ''] + for d in dir_parts[:]: + already_created = [c for c in Directory.childNodes + if c.nodeName == 'Directory' + and c.attributes['LongName'].value == escape(d)] + + if already_created != []: + Directory = already_created[0] + dir_parts.remove(d) + upper_dir += d + else: + break + + for d in dir_parts: + nDirectory = factory.createElement( 'Directory' ) + nDirectory.attributes['LongName'] = escape( d ) + nDirectory.attributes['Name'] = escape( gen_dos_short_file_name( d, filename_set ) ) + upper_dir += d + nDirectory.attributes['Id'] = convert_to_id( upper_dir, id_set ) + + Directory.childNodes.append( nDirectory ) + Directory = nDirectory + + return Directory + + for file in files: + drive, path = os.path.splitdrive( file.PACKAGING_INSTALL_LOCATION ) + filename = os.path.basename( path ) + dirname = os.path.dirname( path ) + + h = { + # tagname : default value + 'PACKAGING_X_MSI_VITAL' : 'yes', + 'PACKAGING_X_MSI_FILEID' : convert_to_id(filename, id_set), + 'PACKAGING_X_MSI_LONGNAME' : filename, + 'PACKAGING_X_MSI_SHORTNAME' : gen_dos_short_file_name(filename, filename_set), + 'PACKAGING_X_MSI_SOURCE' : file.get_path(), + } + + # fill in the default tags given above. + for k,v in [ (k, v) for (k,v) in h.items() if not hasattr(file, k) ]: + setattr( file, k, v ) + + File = factory.createElement( 'File' ) + File.attributes['LongName'] = escape( file.PACKAGING_X_MSI_LONGNAME ) + File.attributes['Name'] = escape( file.PACKAGING_X_MSI_SHORTNAME ) + File.attributes['Source'] = escape( file.PACKAGING_X_MSI_SOURCE ) + File.attributes['Id'] = escape( file.PACKAGING_X_MSI_FILEID ) + File.attributes['Vital'] = escape( file.PACKAGING_X_MSI_VITAL ) + + # create the Tag under which this file should appear + Component = factory.createElement('Component') + Component.attributes['DiskId'] = '1' + Component.attributes['Id'] = convert_to_id( filename, id_set ) + + # hang the component node under the root node and the file node + # under the component node. + Directory = get_directory( root, dirname ) + Directory.childNodes.append( Component ) + Component.childNodes.append( File ) + +# +# additional functions +# +def build_wxsfile_features_section(root, files, NAME, VERSION, SUMMARY, id_set): + """ This function creates the tag based on the supplied xml tree. + + This is achieved by finding all s and adding them to a default target. + + It should be called after the tree has been built completly. We assume + that a MY_DEFAULT_FOLDER Property is defined in the wxs file tree. + + Furthermore a top-level with the name and VERSION of the software will be created. + + An PACKAGING_X_MSI_FEATURE can either be a string, where the feature + DESCRIPTION will be the same as its title or a Tuple, where the first + part will be its title and the second its DESCRIPTION. + """ + factory = Document() + Feature = factory.createElement('Feature') + Feature.attributes['Id'] = 'complete' + Feature.attributes['ConfigurableDirectory'] = 'MY_DEFAULT_FOLDER' + Feature.attributes['Level'] = '1' + Feature.attributes['Title'] = escape( '%s %s' % (NAME, VERSION) ) + Feature.attributes['Description'] = escape( SUMMARY ) + Feature.attributes['Display'] = 'expand' + + for (feature, files) in create_feature_dict(files).items(): + SubFeature = factory.createElement('Feature') + SubFeature.attributes['Level'] = '1' + + if SCons.Util.is_Tuple(feature): + SubFeature.attributes['Id'] = convert_to_id( feature[0], id_set ) + SubFeature.attributes['Title'] = escape(feature[0]) + SubFeature.attributes['Description'] = escape(feature[1]) + else: + SubFeature.attributes['Id'] = convert_to_id( feature, id_set ) + if feature=='default': + SubFeature.attributes['Description'] = 'Main Part' + SubFeature.attributes['Title'] = 'Main Part' + elif feature=='PACKAGING_DOC': + SubFeature.attributes['Description'] = 'Documentation' + SubFeature.attributes['Title'] = 'Documentation' + else: + SubFeature.attributes['Description'] = escape(feature) + SubFeature.attributes['Title'] = escape(feature) + + # build the componentrefs. As one of the design decision is that every + # file is also a component we walk the list of files and create a + # reference. + for f in files: + ComponentRef = factory.createElement('ComponentRef') + ComponentRef.attributes['Id'] = convert_to_id( os.path.basename(f.get_path()), id_set ) + SubFeature.childNodes.append(ComponentRef) + + Feature.childNodes.append(SubFeature) + + root.getElementsByTagName('Product')[0].childNodes.append(Feature) + +def build_wxsfile_default_gui(root): + """ this function adds a default GUI to the wxs file + """ + factory = Document() + Product = root.getElementsByTagName('Product')[0] + + UIRef = factory.createElement('UIRef') + UIRef.attributes['Id'] = 'WixUI_Mondo' + Product.childNodes.append(UIRef) + + UIRef = factory.createElement('UIRef') + UIRef.attributes['Id'] = 'WixUI_ErrorProgressText' + Product.childNodes.append(UIRef) + +def build_license_file(directory, spec): + """ creates a License.rtf file with the content of "X_MSI_LICENSE_TEXT" + in the given directory + """ + name, text = '', '' + + try: + name = spec['LICENSE'] + text = spec['X_MSI_LICENSE_TEXT'] + except KeyError: + pass # ignore this as X_MSI_LICENSE_TEXT is optional + + if name!='' or text!='': + file = open( os.path.join(directory.get_path(), 'License.rtf'), 'w' ) + file.write('{\\rtf') + if text!='': + file.write(text.replace('\n', '\\par ')) + else: + file.write(name+'\\par\\par') + file.write('}') + file.close() + +# +# mandatory and optional package tags +# +def build_wxsfile_header_section(root, spec): + """ Adds the xml file node which define the package meta-data. + """ + # Create the needed DOM nodes and add them at the correct position in the tree. + factory = Document() + Product = factory.createElement( 'Product' ) + Package = factory.createElement( 'Package' ) + + root.childNodes.append( Product ) + Product.childNodes.append( Package ) + + # set "mandatory" default values + if 'X_MSI_LANGUAGE' not in spec: + spec['X_MSI_LANGUAGE'] = '1033' # select english + + # mandatory sections, will throw a KeyError if the tag is not available + Product.attributes['Name'] = escape( spec['NAME'] ) + Product.attributes['Version'] = escape( spec['VERSION'] ) + Product.attributes['Manufacturer'] = escape( spec['VENDOR'] ) + Product.attributes['Language'] = escape( spec['X_MSI_LANGUAGE'] ) + Package.attributes['Description'] = escape( spec['SUMMARY'] ) + + # now the optional tags, for which we avoid the KeyErrror exception + if 'DESCRIPTION' in spec: + Package.attributes['Comments'] = escape( spec['DESCRIPTION'] ) + + if 'X_MSI_UPGRADE_CODE' in spec: + Package.attributes['X_MSI_UPGRADE_CODE'] = escape( spec['X_MSI_UPGRADE_CODE'] ) + + # We hardcode the media tag as our current model cannot handle it. + Media = factory.createElement('Media') + Media.attributes['Id'] = '1' + Media.attributes['Cabinet'] = 'default.cab' + Media.attributes['EmbedCab'] = 'yes' + root.getElementsByTagName('Product')[0].childNodes.append(Media) + +# this builder is the entry-point for .wxs file compiler. +wxs_builder = Builder( + action = Action( build_wxsfile, string_wxsfile ), + ensure_suffix = '.wxs' ) + +def package(env, target, source, PACKAGEROOT, NAME, VERSION, + DESCRIPTION, SUMMARY, VENDOR, X_MSI_LANGUAGE, **kw): + # make sure that the Wix Builder is in the environment + SCons.Tool.Tool('wix').generate(env) + + # get put the keywords for the specfile compiler. These are the arguments + # given to the package function and all optional ones stored in kw, minus + # the the source, target and env one. + loc = locals() + del loc['kw'] + kw.update(loc) + del kw['source'], kw['target'], kw['env'] + + # strip the install builder from the source files + target, source = stripinstallbuilder(target, source, env) + + # put the arguments into the env and call the specfile builder. + env['msi_spec'] = kw + specfile = wxs_builder(* [env, target, source], **kw) + + # now call the WiX Tool with the built specfile added as a source. + msifile = env.WiX(target, specfile) + + # return the target and source tuple. + return (msifile, source+[specfile]) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/packaging/rpm.py b/scons/scons-local-2.3.0/SCons/Tool/packaging/rpm.py new file mode 100644 index 000000000..d60166f7c --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/packaging/rpm.py @@ -0,0 +1,357 @@ +"""SCons.Tool.Packaging.rpm + +The rpm packager. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Tool/packaging/rpm.py 2013/03/03 09:48:35 garyo" + +import os + +import SCons.Builder +import SCons.Tool.rpmutils + +from SCons.Environment import OverrideEnvironment +from SCons.Tool.packaging import stripinstallbuilder, src_targz +from SCons.Errors import UserError + +def package(env, target, source, PACKAGEROOT, NAME, VERSION, + PACKAGEVERSION, DESCRIPTION, SUMMARY, X_RPM_GROUP, LICENSE, + **kw): + # initialize the rpm tool + SCons.Tool.Tool('rpm').generate(env) + + bld = env['BUILDERS']['Rpm'] + + # Generate a UserError whenever the target name has been set explicitly, + # since rpm does not allow for controlling it. This is detected by + # checking if the target has been set to the default by the Package() + # Environment function. + if str(target[0])!="%s-%s"%(NAME, VERSION): + raise UserError( "Setting target is not supported for rpm." ) + else: + # This should be overridable from the construction environment, + # which it is by using ARCHITECTURE=. + buildarchitecture = SCons.Tool.rpmutils.defaultMachine() + + if 'ARCHITECTURE' in kw: + buildarchitecture = kw['ARCHITECTURE'] + + fmt = '%s-%s-%s.%s.rpm' + srcrpm = fmt % (NAME, VERSION, PACKAGEVERSION, 'src') + binrpm = fmt % (NAME, VERSION, PACKAGEVERSION, buildarchitecture) + + target = [ srcrpm, binrpm ] + + # get the correct arguments into the kw hash + loc=locals() + del loc['kw'] + kw.update(loc) + del kw['source'], kw['target'], kw['env'] + + # if no "SOURCE_URL" tag is given add a default one. + if 'SOURCE_URL' not in kw: + #kw['SOURCE_URL']=(str(target[0])+".tar.gz").replace('.rpm', '') + kw['SOURCE_URL']=(str(target[0])+".tar.gz").replace('.rpm', '') + + # mangle the source and target list for the rpmbuild + env = OverrideEnvironment(env, kw) + target, source = stripinstallbuilder(target, source, env) + target, source = addspecfile(target, source, env) + target, source = collectintargz(target, source, env) + + # now call the rpm builder to actually build the packet. + return bld(env, target, source, **kw) + +def collectintargz(target, source, env): + """ Puts all source files into a tar.gz file. """ + # the rpm tool depends on a source package, until this is chagned + # this hack needs to be here that tries to pack all sources in. + sources = env.FindSourceFiles() + + # filter out the target we are building the source list for. + #sources = [s for s in sources if not (s in target)] + sources = [s for s in sources if s not in target] + + # find the .spec file for rpm and add it since it is not necessarily found + # by the FindSourceFiles function. + #sources.extend( [s for s in source if str(s).rfind('.spec')!=-1] ) + spec_file = lambda s: str(s).rfind('.spec') != -1 + sources.extend( list(filter(spec_file, source)) ) + + # as the source contains the url of the source package this rpm package + # is built from, we extract the target name + #tarball = (str(target[0])+".tar.gz").replace('.rpm', '') + tarball = (str(target[0])+".tar.gz").replace('.rpm', '') + try: + #tarball = env['SOURCE_URL'].split('/')[-1] + tarball = env['SOURCE_URL'].split('/')[-1] + except KeyError, e: + raise SCons.Errors.UserError( "Missing PackageTag '%s' for RPM packager" % e.args[0] ) + + tarball = src_targz.package(env, source=sources, target=tarball, + PACKAGEROOT=env['PACKAGEROOT'], ) + + return (target, tarball) + +def addspecfile(target, source, env): + specfile = "%s-%s" % (env['NAME'], env['VERSION']) + + bld = SCons.Builder.Builder(action = build_specfile, + suffix = '.spec', + target_factory = SCons.Node.FS.File) + + source.extend(bld(env, specfile, source)) + + return (target,source) + +def build_specfile(target, source, env): + """ Builds a RPM specfile from a dictionary with string metadata and + by analyzing a tree of nodes. + """ + file = open(target[0].abspath, 'w') + str = "" + + try: + file.write( build_specfile_header(env) ) + file.write( build_specfile_sections(env) ) + file.write( build_specfile_filesection(env, source) ) + file.close() + + # call a user specified function + if 'CHANGE_SPECFILE' in env: + env['CHANGE_SPECFILE'](target, source) + + except KeyError, e: + raise SCons.Errors.UserError( '"%s" package field for RPM is missing.' % e.args[0] ) + + +# +# mandatory and optional package tag section +# +def build_specfile_sections(spec): + """ Builds the sections of a rpm specfile. + """ + str = "" + + mandatory_sections = { + 'DESCRIPTION' : '\n%%description\n%s\n\n', } + + str = str + SimpleTagCompiler(mandatory_sections).compile( spec ) + + optional_sections = { + 'DESCRIPTION_' : '%%description -l %s\n%s\n\n', + 'CHANGELOG' : '%%changelog\n%s\n\n', + 'X_RPM_PREINSTALL' : '%%pre\n%s\n\n', + 'X_RPM_POSTINSTALL' : '%%post\n%s\n\n', + 'X_RPM_PREUNINSTALL' : '%%preun\n%s\n\n', + 'X_RPM_POSTUNINSTALL' : '%%postun\n%s\n\n', + 'X_RPM_VERIFY' : '%%verify\n%s\n\n', + + # These are for internal use but could possibly be overriden + 'X_RPM_PREP' : '%%prep\n%s\n\n', + 'X_RPM_BUILD' : '%%build\n%s\n\n', + 'X_RPM_INSTALL' : '%%install\n%s\n\n', + 'X_RPM_CLEAN' : '%%clean\n%s\n\n', + } + + # Default prep, build, install and clean rules + # TODO: optimize those build steps, to not compile the project a second time + if 'X_RPM_PREP' not in spec: + spec['X_RPM_PREP'] = '[ -n "$RPM_BUILD_ROOT" -a "$RPM_BUILD_ROOT" != / ] && rm -rf "$RPM_BUILD_ROOT"' + '\n%setup -q' + + if 'X_RPM_BUILD' not in spec: + spec['X_RPM_BUILD'] = 'mkdir "$RPM_BUILD_ROOT"' + + if 'X_RPM_INSTALL' not in spec: + spec['X_RPM_INSTALL'] = 'scons --install-sandbox="$RPM_BUILD_ROOT" "$RPM_BUILD_ROOT"' + + if 'X_RPM_CLEAN' not in spec: + spec['X_RPM_CLEAN'] = '[ -n "$RPM_BUILD_ROOT" -a "$RPM_BUILD_ROOT" != / ] && rm -rf "$RPM_BUILD_ROOT"' + + str = str + SimpleTagCompiler(optional_sections, mandatory=0).compile( spec ) + + return str + +def build_specfile_header(spec): + """ Builds all section but the %file of a rpm specfile + """ + str = "" + + # first the mandatory sections + mandatory_header_fields = { + 'NAME' : '%%define name %s\nName: %%{name}\n', + 'VERSION' : '%%define version %s\nVersion: %%{version}\n', + 'PACKAGEVERSION' : '%%define release %s\nRelease: %%{release}\n', + 'X_RPM_GROUP' : 'Group: %s\n', + 'SUMMARY' : 'Summary: %s\n', + 'LICENSE' : 'License: %s\n', } + + str = str + SimpleTagCompiler(mandatory_header_fields).compile( spec ) + + # now the optional tags + optional_header_fields = { + 'VENDOR' : 'Vendor: %s\n', + 'X_RPM_URL' : 'Url: %s\n', + 'SOURCE_URL' : 'Source: %s\n', + 'SUMMARY_' : 'Summary(%s): %s\n', + 'X_RPM_DISTRIBUTION' : 'Distribution: %s\n', + 'X_RPM_ICON' : 'Icon: %s\n', + 'X_RPM_PACKAGER' : 'Packager: %s\n', + 'X_RPM_GROUP_' : 'Group(%s): %s\n', + + 'X_RPM_REQUIRES' : 'Requires: %s\n', + 'X_RPM_PROVIDES' : 'Provides: %s\n', + 'X_RPM_CONFLICTS' : 'Conflicts: %s\n', + 'X_RPM_BUILDREQUIRES' : 'BuildRequires: %s\n', + + 'X_RPM_SERIAL' : 'Serial: %s\n', + 'X_RPM_EPOCH' : 'Epoch: %s\n', + 'X_RPM_AUTOREQPROV' : 'AutoReqProv: %s\n', + 'X_RPM_EXCLUDEARCH' : 'ExcludeArch: %s\n', + 'X_RPM_EXCLUSIVEARCH' : 'ExclusiveArch: %s\n', + 'X_RPM_PREFIX' : 'Prefix: %s\n', + 'X_RPM_CONFLICTS' : 'Conflicts: %s\n', + + # internal use + 'X_RPM_BUILDROOT' : 'BuildRoot: %s\n', } + + # fill in default values: + # Adding a BuildRequires renders the .rpm unbuildable under System, which + # are not managed by rpm, since the database to resolve this dependency is + # missing (take Gentoo as an example) +# if not s.has_key('x_rpm_BuildRequires'): +# s['x_rpm_BuildRequires'] = 'scons' + + if 'X_RPM_BUILDROOT' not in spec: + spec['X_RPM_BUILDROOT'] = '%{_tmppath}/%{name}-%{version}-%{release}' + + str = str + SimpleTagCompiler(optional_header_fields, mandatory=0).compile( spec ) + return str + +# +# mandatory and optional file tags +# +def build_specfile_filesection(spec, files): + """ builds the %file section of the specfile + """ + str = '%files\n' + + if 'X_RPM_DEFATTR' not in spec: + spec['X_RPM_DEFATTR'] = '(-,root,root)' + + str = str + '%%defattr %s\n' % spec['X_RPM_DEFATTR'] + + supported_tags = { + 'PACKAGING_CONFIG' : '%%config %s', + 'PACKAGING_CONFIG_NOREPLACE' : '%%config(noreplace) %s', + 'PACKAGING_DOC' : '%%doc %s', + 'PACKAGING_UNIX_ATTR' : '%%attr %s', + 'PACKAGING_LANG_' : '%%lang(%s) %s', + 'PACKAGING_X_RPM_VERIFY' : '%%verify %s', + 'PACKAGING_X_RPM_DIR' : '%%dir %s', + 'PACKAGING_X_RPM_DOCDIR' : '%%docdir %s', + 'PACKAGING_X_RPM_GHOST' : '%%ghost %s', } + + for file in files: + # build the tagset + tags = {} + for k in supported_tags.keys(): + try: + tags[k]=getattr(file, k) + except AttributeError: + pass + + # compile the tagset + str = str + SimpleTagCompiler(supported_tags, mandatory=0).compile( tags ) + + str = str + ' ' + str = str + file.PACKAGING_INSTALL_LOCATION + str = str + '\n\n' + + return str + +class SimpleTagCompiler(object): + """ This class is a simple string substition utility: + the replacement specfication is stored in the tagset dictionary, something + like: + { "abc" : "cdef %s ", + "abc_" : "cdef %s %s" } + + the compile function gets a value dictionary, which may look like: + { "abc" : "ghij", + "abc_gh" : "ij" } + + The resulting string will be: + "cdef ghij cdef gh ij" + """ + def __init__(self, tagset, mandatory=1): + self.tagset = tagset + self.mandatory = mandatory + + def compile(self, values): + """ compiles the tagset and returns a str containing the result + """ + def is_international(tag): + #return tag.endswith('_') + return tag[-1:] == '_' + + def get_country_code(tag): + return tag[-2:] + + def strip_country_code(tag): + return tag[:-2] + + replacements = list(self.tagset.items()) + + str = "" + #domestic = [ (k,v) for k,v in replacements if not is_international(k) ] + domestic = [t for t in replacements if not is_international(t[0])] + for key, replacement in domestic: + try: + str = str + replacement % values[key] + except KeyError, e: + if self.mandatory: + raise e + + #international = [ (k,v) for k,v in replacements if is_international(k) ] + international = [t for t in replacements if is_international(t[0])] + for key, replacement in international: + try: + #int_values_for_key = [ (get_country_code(k),v) for k,v in values.items() if strip_country_code(k) == key ] + x = [t for t in values.items() if strip_country_code(t[0]) == key] + int_values_for_key = [(get_country_code(t[0]),t[1]) for t in x] + for v in int_values_for_key: + str = str + replacement % v + except KeyError, e: + if self.mandatory: + raise e + + return str + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/packaging/src_tarbz2.py b/scons/scons-local-2.3.0/SCons/Tool/packaging/src_tarbz2.py new file mode 100644 index 000000000..1da2becae --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/packaging/src_tarbz2.py @@ -0,0 +1,43 @@ +"""SCons.Tool.Packaging.tarbz2 + +The tarbz2 SRC packager. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/packaging/src_tarbz2.py 2013/03/03 09:48:35 garyo" + +from SCons.Tool.packaging import putintopackageroot + +def package(env, target, source, PACKAGEROOT, **kw): + bld = env['BUILDERS']['Tar'] + bld.set_suffix('.tar.bz2') + target, source = putintopackageroot(target, source, env, PACKAGEROOT, honor_install_location=0) + return bld(env, target, source, TARFLAGS='-jc') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/packaging/src_targz.py b/scons/scons-local-2.3.0/SCons/Tool/packaging/src_targz.py new file mode 100644 index 000000000..bde3316fe --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/packaging/src_targz.py @@ -0,0 +1,43 @@ +"""SCons.Tool.Packaging.targz + +The targz SRC packager. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/packaging/src_targz.py 2013/03/03 09:48:35 garyo" + +from SCons.Tool.packaging import putintopackageroot + +def package(env, target, source, PACKAGEROOT, **kw): + bld = env['BUILDERS']['Tar'] + bld.set_suffix('.tar.gz') + target, source = putintopackageroot(target, source, env, PACKAGEROOT, honor_install_location=0) + return bld(env, target, source, TARFLAGS='-zc') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/packaging/src_zip.py b/scons/scons-local-2.3.0/SCons/Tool/packaging/src_zip.py new file mode 100644 index 000000000..e5a752e36 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/packaging/src_zip.py @@ -0,0 +1,43 @@ +"""SCons.Tool.Packaging.zip + +The zip SRC packager. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/packaging/src_zip.py 2013/03/03 09:48:35 garyo" + +from SCons.Tool.packaging import putintopackageroot + +def package(env, target, source, PACKAGEROOT, **kw): + bld = env['BUILDERS']['Zip'] + bld.set_suffix('.zip') + target, source = putintopackageroot(target, source, env, PACKAGEROOT, honor_install_location=0) + return bld(env, target, source) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/packaging/tarbz2.py b/scons/scons-local-2.3.0/SCons/Tool/packaging/tarbz2.py new file mode 100644 index 000000000..0c8e4e768 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/packaging/tarbz2.py @@ -0,0 +1,44 @@ +"""SCons.Tool.Packaging.tarbz2 + +The tarbz2 SRC packager. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/packaging/tarbz2.py 2013/03/03 09:48:35 garyo" + +from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot + +def package(env, target, source, PACKAGEROOT, **kw): + bld = env['BUILDERS']['Tar'] + bld.set_suffix('.tar.gz') + target, source = putintopackageroot(target, source, env, PACKAGEROOT) + target, source = stripinstallbuilder(target, source, env) + return bld(env, target, source, TARFLAGS='-jc') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/packaging/targz.py b/scons/scons-local-2.3.0/SCons/Tool/packaging/targz.py new file mode 100644 index 000000000..32b74cfb9 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/packaging/targz.py @@ -0,0 +1,44 @@ +"""SCons.Tool.Packaging.targz + +The targz SRC packager. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/packaging/targz.py 2013/03/03 09:48:35 garyo" + +from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot + +def package(env, target, source, PACKAGEROOT, **kw): + bld = env['BUILDERS']['Tar'] + bld.set_suffix('.tar.gz') + target, source = stripinstallbuilder(target, source, env) + target, source = putintopackageroot(target, source, env, PACKAGEROOT) + return bld(env, target, source, TARFLAGS='-zc') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/packaging/zip.py b/scons/scons-local-2.3.0/SCons/Tool/packaging/zip.py new file mode 100644 index 000000000..1f34581e1 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/packaging/zip.py @@ -0,0 +1,44 @@ +"""SCons.Tool.Packaging.zip + +The zip SRC packager. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/packaging/zip.py 2013/03/03 09:48:35 garyo" + +from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot + +def package(env, target, source, PACKAGEROOT, **kw): + bld = env['BUILDERS']['Zip'] + bld.set_suffix('.zip') + target, source = stripinstallbuilder(target, source, env) + target, source = putintopackageroot(target, source, env, PACKAGEROOT) + return bld(env, target, source) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/pdf.py b/scons/scons-local-2.3.0/SCons/Tool/pdf.py new file mode 100644 index 000000000..1b68f107b --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/pdf.py @@ -0,0 +1,78 @@ +"""SCons.Tool.pdf + +Common PDF Builder definition for various other Tool modules that use it. +Add an explicit action to run epstopdf to convert .eps files to .pdf + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/pdf.py 2013/03/03 09:48:35 garyo" + +import SCons.Builder +import SCons.Tool + +PDFBuilder = None + +EpsPdfAction = SCons.Action.Action('$EPSTOPDFCOM', '$EPSTOPDFCOMSTR') + +def generate(env): + try: + env['BUILDERS']['PDF'] + except KeyError: + global PDFBuilder + if PDFBuilder is None: + PDFBuilder = SCons.Builder.Builder(action = {}, + source_scanner = SCons.Tool.PDFLaTeXScanner, + prefix = '$PDFPREFIX', + suffix = '$PDFSUFFIX', + emitter = {}, + source_ext_match = None, + single_source=True) + env['BUILDERS']['PDF'] = PDFBuilder + + env['PDFPREFIX'] = '' + env['PDFSUFFIX'] = '.pdf' + +# put the epstopdf builder in this routine so we can add it after +# the pdftex builder so that one is the default for no source suffix +def generate2(env): + bld = env['BUILDERS']['PDF'] + #bld.add_action('.ps', EpsPdfAction) # this is covered by direct Ghostcript action in gs.py + bld.add_action('.eps', EpsPdfAction) + + env['EPSTOPDF'] = 'epstopdf' + env['EPSTOPDFFLAGS'] = SCons.Util.CLVar('') + env['EPSTOPDFCOM'] = '$EPSTOPDF $EPSTOPDFFLAGS ${SOURCE} --outfile=${TARGET}' + +def exists(env): + # This only puts a skeleton Builder in place, so if someone + # references this Tool directly, it's always "available." + return 1 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/pdflatex.py b/scons/scons-local-2.3.0/SCons/Tool/pdflatex.py new file mode 100644 index 000000000..80756d184 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/pdflatex.py @@ -0,0 +1,84 @@ +"""SCons.Tool.pdflatex + +Tool-specific initialization for pdflatex. +Generates .pdf files from .latex or .ltx files + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/pdflatex.py 2013/03/03 09:48:35 garyo" + +import SCons.Action +import SCons.Util +import SCons.Tool.pdf +import SCons.Tool.tex + +PDFLaTeXAction = None + +def PDFLaTeXAuxFunction(target = None, source= None, env=None): + result = SCons.Tool.tex.InternalLaTeXAuxAction( PDFLaTeXAction, target, source, env ) + if result != 0: + SCons.Tool.tex.check_file_error_message(env['PDFLATEX']) + return result + +PDFLaTeXAuxAction = None + +def generate(env): + """Add Builders and construction variables for pdflatex to an Environment.""" + global PDFLaTeXAction + if PDFLaTeXAction is None: + PDFLaTeXAction = SCons.Action.Action('$PDFLATEXCOM', '$PDFLATEXCOMSTR') + + global PDFLaTeXAuxAction + if PDFLaTeXAuxAction is None: + PDFLaTeXAuxAction = SCons.Action.Action(PDFLaTeXAuxFunction, + strfunction=SCons.Tool.tex.TeXLaTeXStrFunction) + + env.AppendUnique(LATEXSUFFIXES=SCons.Tool.LaTeXSuffixes) + + import pdf + pdf.generate(env) + + bld = env['BUILDERS']['PDF'] + bld.add_action('.ltx', PDFLaTeXAuxAction) + bld.add_action('.latex', PDFLaTeXAuxAction) + bld.add_emitter('.ltx', SCons.Tool.tex.tex_pdf_emitter) + bld.add_emitter('.latex', SCons.Tool.tex.tex_pdf_emitter) + + SCons.Tool.tex.generate_common(env) + +def exists(env): + SCons.Tool.tex.generate_darwin(env) + return env.Detect('pdflatex') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/pdftex.py b/scons/scons-local-2.3.0/SCons/Tool/pdftex.py new file mode 100644 index 000000000..34ca325b0 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/pdftex.py @@ -0,0 +1,109 @@ +"""SCons.Tool.pdftex + +Tool-specific initialization for pdftex. +Generates .pdf files from .tex files + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/pdftex.py 2013/03/03 09:48:35 garyo" + +import os +import SCons.Action +import SCons.Util +import SCons.Tool.tex + +PDFTeXAction = None + +# This action might be needed more than once if we are dealing with +# labels and bibtex. +PDFLaTeXAction = None + +def PDFLaTeXAuxAction(target = None, source= None, env=None): + result = SCons.Tool.tex.InternalLaTeXAuxAction( PDFLaTeXAction, target, source, env ) + return result + +def PDFTeXLaTeXFunction(target = None, source= None, env=None): + """A builder for TeX and LaTeX that scans the source file to + decide the "flavor" of the source and then executes the appropriate + program.""" + basedir = os.path.split(str(source[0]))[0] + abspath = os.path.abspath(basedir) + + if SCons.Tool.tex.is_LaTeX(source,env,abspath): + result = PDFLaTeXAuxAction(target,source,env) + if result != 0: + SCons.Tool.tex.check_file_error_message(env['PDFLATEX']) + else: + result = PDFTeXAction(target,source,env) + if result != 0: + SCons.Tool.tex.check_file_error_message(env['PDFTEX']) + return result + +PDFTeXLaTeXAction = None + +def generate(env): + """Add Builders and construction variables for pdftex to an Environment.""" + global PDFTeXAction + if PDFTeXAction is None: + PDFTeXAction = SCons.Action.Action('$PDFTEXCOM', '$PDFTEXCOMSTR') + + global PDFLaTeXAction + if PDFLaTeXAction is None: + PDFLaTeXAction = SCons.Action.Action("$PDFLATEXCOM", "$PDFLATEXCOMSTR") + + global PDFTeXLaTeXAction + if PDFTeXLaTeXAction is None: + PDFTeXLaTeXAction = SCons.Action.Action(PDFTeXLaTeXFunction, + strfunction=SCons.Tool.tex.TeXLaTeXStrFunction) + + env.AppendUnique(LATEXSUFFIXES=SCons.Tool.LaTeXSuffixes) + + import pdf + pdf.generate(env) + + bld = env['BUILDERS']['PDF'] + bld.add_action('.tex', PDFTeXLaTeXAction) + bld.add_emitter('.tex', SCons.Tool.tex.tex_pdf_emitter) + + # Add the epstopdf builder after the pdftex builder + # so pdftex is the default for no source suffix + pdf.generate2(env) + + SCons.Tool.tex.generate_common(env) + +def exists(env): + SCons.Tool.tex.generate_darwin(env) + return env.Detect('pdftex') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/qt.py b/scons/scons-local-2.3.0/SCons/Tool/qt.py new file mode 100644 index 000000000..96e14b1cf --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/qt.py @@ -0,0 +1,336 @@ + +"""SCons.Tool.qt + +Tool-specific initialization for Qt. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/qt.py 2013/03/03 09:48:35 garyo" + +import os.path +import re + +import SCons.Action +import SCons.Builder +import SCons.Defaults +import SCons.Scanner +import SCons.Tool +import SCons.Util + +class ToolQtWarning(SCons.Warnings.Warning): + pass + +class GeneratedMocFileNotIncluded(ToolQtWarning): + pass + +class QtdirNotFound(ToolQtWarning): + pass + +SCons.Warnings.enableWarningClass(ToolQtWarning) + +header_extensions = [".h", ".hxx", ".hpp", ".hh"] +if SCons.Util.case_sensitive_suffixes('.h', '.H'): + header_extensions.append('.H') +cplusplus = __import__('c++', globals(), locals(), []) +cxx_suffixes = cplusplus.CXXSuffixes + +def checkMocIncluded(target, source, env): + moc = target[0] + cpp = source[0] + # looks like cpp.includes is cleared before the build stage :-( + # not really sure about the path transformations (moc.cwd? cpp.cwd?) :-/ + path = SCons.Defaults.CScan.path(env, moc.cwd) + includes = SCons.Defaults.CScan(cpp, env, path) + if not moc in includes: + SCons.Warnings.warn( + GeneratedMocFileNotIncluded, + "Generated moc file '%s' is not included by '%s'" % + (str(moc), str(cpp))) + +def find_file(filename, paths, node_factory): + for dir in paths: + node = node_factory(filename, dir) + if node.rexists(): + return node + return None + +class _Automoc(object): + """ + Callable class, which works as an emitter for Programs, SharedLibraries and + StaticLibraries. + """ + + def __init__(self, objBuilderName): + self.objBuilderName = objBuilderName + + def __call__(self, target, source, env): + """ + Smart autoscan function. Gets the list of objects for the Program + or Lib. Adds objects and builders for the special qt files. + """ + try: + if int(env.subst('$QT_AUTOSCAN')) == 0: + return target, source + except ValueError: + pass + try: + debug = int(env.subst('$QT_DEBUG')) + except ValueError: + debug = 0 + + # some shortcuts used in the scanner + splitext = SCons.Util.splitext + objBuilder = getattr(env, self.objBuilderName) + + # some regular expressions: + # Q_OBJECT detection + q_object_search = re.compile(r'[^A-Za-z0-9]Q_OBJECT[^A-Za-z0-9]') + # cxx and c comment 'eater' + #comment = re.compile(r'(//.*)|(/\*(([^*])|(\*[^/]))*\*/)') + # CW: something must be wrong with the regexp. See also bug #998222 + # CURRENTLY THERE IS NO TEST CASE FOR THAT + + # The following is kind of hacky to get builders working properly (FIXME) + objBuilderEnv = objBuilder.env + objBuilder.env = env + mocBuilderEnv = env.Moc.env + env.Moc.env = env + + # make a deep copy for the result; MocH objects will be appended + out_sources = source[:] + + for obj in source: + if not obj.has_builder(): + # binary obj file provided + if debug: + print "scons: qt: '%s' seems to be a binary. Discarded." % str(obj) + continue + cpp = obj.sources[0] + if not splitext(str(cpp))[1] in cxx_suffixes: + if debug: + print "scons: qt: '%s' is no cxx file. Discarded." % str(cpp) + # c or fortran source + continue + #cpp_contents = comment.sub('', cpp.get_text_contents()) + cpp_contents = cpp.get_text_contents() + h=None + for h_ext in header_extensions: + # try to find the header file in the corresponding source + # directory + hname = splitext(cpp.name)[0] + h_ext + h = find_file(hname, (cpp.get_dir(),), env.File) + if h: + if debug: + print "scons: qt: Scanning '%s' (header of '%s')" % (str(h), str(cpp)) + #h_contents = comment.sub('', h.get_text_contents()) + h_contents = h.get_text_contents() + break + if not h and debug: + print "scons: qt: no header for '%s'." % (str(cpp)) + if h and q_object_search.search(h_contents): + # h file with the Q_OBJECT macro found -> add moc_cpp + moc_cpp = env.Moc(h) + moc_o = objBuilder(moc_cpp) + out_sources.append(moc_o) + #moc_cpp.target_scanner = SCons.Defaults.CScan + if debug: + print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(h), str(moc_cpp)) + if cpp and q_object_search.search(cpp_contents): + # cpp file with Q_OBJECT macro found -> add moc + # (to be included in cpp) + moc = env.Moc(cpp) + env.Ignore(moc, moc) + if debug: + print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(cpp), str(moc)) + #moc.source_scanner = SCons.Defaults.CScan + # restore the original env attributes (FIXME) + objBuilder.env = objBuilderEnv + env.Moc.env = mocBuilderEnv + + return (target, out_sources) + +AutomocShared = _Automoc('SharedObject') +AutomocStatic = _Automoc('StaticObject') + +def _detect(env): + """Not really safe, but fast method to detect the QT library""" + QTDIR = None + if not QTDIR: + QTDIR = env.get('QTDIR',None) + if not QTDIR: + QTDIR = os.environ.get('QTDIR',None) + if not QTDIR: + moc = env.WhereIs('moc') + if moc: + QTDIR = os.path.dirname(os.path.dirname(moc)) + SCons.Warnings.warn( + QtdirNotFound, + "Could not detect qt, using moc executable as a hint (QTDIR=%s)" % QTDIR) + else: + QTDIR = None + SCons.Warnings.warn( + QtdirNotFound, + "Could not detect qt, using empty QTDIR") + return QTDIR + +def uicEmitter(target, source, env): + adjustixes = SCons.Util.adjustixes + bs = SCons.Util.splitext(str(source[0].name))[0] + bs = os.path.join(str(target[0].get_dir()),bs) + # first target (header) is automatically added by builder + if len(target) < 2: + # second target is implementation + target.append(adjustixes(bs, + env.subst('$QT_UICIMPLPREFIX'), + env.subst('$QT_UICIMPLSUFFIX'))) + if len(target) < 3: + # third target is moc file + target.append(adjustixes(bs, + env.subst('$QT_MOCHPREFIX'), + env.subst('$QT_MOCHSUFFIX'))) + return target, source + +def uicScannerFunc(node, env, path): + lookout = [] + lookout.extend(env['CPPPATH']) + lookout.append(str(node.rfile().dir)) + includes = re.findall("(.*?)", node.get_text_contents()) + result = [] + for incFile in includes: + dep = env.FindFile(incFile,lookout) + if dep: + result.append(dep) + return result + +uicScanner = SCons.Scanner.Base(uicScannerFunc, + name = "UicScanner", + node_class = SCons.Node.FS.File, + node_factory = SCons.Node.FS.File, + recursive = 0) + +def generate(env): + """Add Builders and construction variables for qt to an Environment.""" + CLVar = SCons.Util.CLVar + Action = SCons.Action.Action + Builder = SCons.Builder.Builder + + env.SetDefault(QTDIR = _detect(env), + QT_BINPATH = os.path.join('$QTDIR', 'bin'), + QT_CPPPATH = os.path.join('$QTDIR', 'include'), + QT_LIBPATH = os.path.join('$QTDIR', 'lib'), + QT_MOC = os.path.join('$QT_BINPATH','moc'), + QT_UIC = os.path.join('$QT_BINPATH','uic'), + QT_LIB = 'qt', # may be set to qt-mt + + QT_AUTOSCAN = 1, # scan for moc'able sources + + # Some QT specific flags. I don't expect someone wants to + # manipulate those ... + QT_UICIMPLFLAGS = CLVar(''), + QT_UICDECLFLAGS = CLVar(''), + QT_MOCFROMHFLAGS = CLVar(''), + QT_MOCFROMCXXFLAGS = CLVar('-i'), + + # suffixes/prefixes for the headers / sources to generate + QT_UICDECLPREFIX = '', + QT_UICDECLSUFFIX = '.h', + QT_UICIMPLPREFIX = 'uic_', + QT_UICIMPLSUFFIX = '$CXXFILESUFFIX', + QT_MOCHPREFIX = 'moc_', + QT_MOCHSUFFIX = '$CXXFILESUFFIX', + QT_MOCCXXPREFIX = '', + QT_MOCCXXSUFFIX = '.moc', + QT_UISUFFIX = '.ui', + + # Commands for the qt support ... + # command to generate header, implementation and moc-file + # from a .ui file + QT_UICCOM = [ + CLVar('$QT_UIC $QT_UICDECLFLAGS -o ${TARGETS[0]} $SOURCE'), + CLVar('$QT_UIC $QT_UICIMPLFLAGS -impl ${TARGETS[0].file} ' + '-o ${TARGETS[1]} $SOURCE'), + CLVar('$QT_MOC $QT_MOCFROMHFLAGS -o ${TARGETS[2]} ${TARGETS[0]}')], + # command to generate meta object information for a class + # declarated in a header + QT_MOCFROMHCOM = ( + '$QT_MOC $QT_MOCFROMHFLAGS -o ${TARGETS[0]} $SOURCE'), + # command to generate meta object information for a class + # declarated in a cpp file + QT_MOCFROMCXXCOM = [ + CLVar('$QT_MOC $QT_MOCFROMCXXFLAGS -o ${TARGETS[0]} $SOURCE'), + Action(checkMocIncluded,None)]) + + # ... and the corresponding builders + uicBld = Builder(action=SCons.Action.Action('$QT_UICCOM', '$QT_UICCOMSTR'), + emitter=uicEmitter, + src_suffix='$QT_UISUFFIX', + suffix='$QT_UICDECLSUFFIX', + prefix='$QT_UICDECLPREFIX', + source_scanner=uicScanner) + mocBld = Builder(action={}, prefix={}, suffix={}) + for h in header_extensions: + act = SCons.Action.Action('$QT_MOCFROMHCOM', '$QT_MOCFROMHCOMSTR') + mocBld.add_action(h, act) + mocBld.prefix[h] = '$QT_MOCHPREFIX' + mocBld.suffix[h] = '$QT_MOCHSUFFIX' + for cxx in cxx_suffixes: + act = SCons.Action.Action('$QT_MOCFROMCXXCOM', '$QT_MOCFROMCXXCOMSTR') + mocBld.add_action(cxx, act) + mocBld.prefix[cxx] = '$QT_MOCCXXPREFIX' + mocBld.suffix[cxx] = '$QT_MOCCXXSUFFIX' + + # register the builders + env['BUILDERS']['Uic'] = uicBld + env['BUILDERS']['Moc'] = mocBld + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + static_obj.add_src_builder('Uic') + shared_obj.add_src_builder('Uic') + + # We use the emitters of Program / StaticLibrary / SharedLibrary + # to scan for moc'able files + # We can't refer to the builders directly, we have to fetch them + # as Environment attributes because that sets them up to be called + # correctly later by our emitter. + env.AppendUnique(PROGEMITTER =[AutomocStatic], + SHLIBEMITTER=[AutomocShared], + LIBEMITTER =[AutomocStatic], + # Of course, we need to link against the qt libraries + CPPPATH=["$QT_CPPPATH"], + LIBPATH=["$QT_LIBPATH"], + LIBS=['$QT_LIB']) + +def exists(env): + return _detect(env) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/rmic.py b/scons/scons-local-2.3.0/SCons/Tool/rmic.py new file mode 100644 index 000000000..df90bc081 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/rmic.py @@ -0,0 +1,126 @@ +"""SCons.Tool.rmic + +Tool-specific initialization for rmic. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/rmic.py 2013/03/03 09:48:35 garyo" + +import os.path + +import SCons.Action +import SCons.Builder +import SCons.Node.FS +import SCons.Util + +def emit_rmic_classes(target, source, env): + """Create and return lists of Java RMI stub and skeleton + class files to be created from a set of class files. + """ + class_suffix = env.get('JAVACLASSSUFFIX', '.class') + classdir = env.get('JAVACLASSDIR') + + if not classdir: + try: + s = source[0] + except IndexError: + classdir = '.' + else: + try: + classdir = s.attributes.java_classdir + except AttributeError: + classdir = '.' + classdir = env.Dir(classdir).rdir() + if str(classdir) == '.': + c_ = None + else: + c_ = str(classdir) + os.sep + + slist = [] + for src in source: + try: + classname = src.attributes.java_classname + except AttributeError: + classname = str(src) + if c_ and classname[:len(c_)] == c_: + classname = classname[len(c_):] + if class_suffix and classname[:-len(class_suffix)] == class_suffix: + classname = classname[-len(class_suffix):] + s = src.rfile() + s.attributes.java_classdir = classdir + s.attributes.java_classname = classname + slist.append(s) + + stub_suffixes = ['_Stub'] + if env.get('JAVAVERSION') == '1.4': + stub_suffixes.append('_Skel') + + tlist = [] + for s in source: + for suff in stub_suffixes: + fname = s.attributes.java_classname.replace('.', os.sep) + \ + suff + class_suffix + t = target[0].File(fname) + t.attributes.java_lookupdir = target[0] + tlist.append(t) + + return tlist, source + +RMICAction = SCons.Action.Action('$RMICCOM', '$RMICCOMSTR') + +RMICBuilder = SCons.Builder.Builder(action = RMICAction, + emitter = emit_rmic_classes, + src_suffix = '$JAVACLASSSUFFIX', + target_factory = SCons.Node.FS.Dir, + source_factory = SCons.Node.FS.File) + +def generate(env): + """Add Builders and construction variables for rmic to an Environment.""" + env['BUILDERS']['RMIC'] = RMICBuilder + + env['RMIC'] = 'rmic' + env['RMICFLAGS'] = SCons.Util.CLVar('') + env['RMICCOM'] = '$RMIC $RMICFLAGS -d ${TARGET.attributes.java_lookupdir} -classpath ${SOURCE.attributes.java_classdir} ${SOURCES.attributes.java_classname}' + env['JAVACLASSSUFFIX'] = '.class' + +def exists(env): + # As reported by Jan Nijtmans in issue #2730, the simple + # return env.Detect('rmic') + # doesn't always work during initialization. For now, we + # stop trying to detect an executable (analogous to the + # javac Builder). + # TODO: Come up with a proper detect() routine...and enable it. + return 1 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/rpcgen.py b/scons/scons-local-2.3.0/SCons/Tool/rpcgen.py new file mode 100644 index 000000000..309db8299 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/rpcgen.py @@ -0,0 +1,70 @@ +"""SCons.Tool.rpcgen + +Tool-specific initialization for RPCGEN tools. + +Three normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/rpcgen.py 2013/03/03 09:48:35 garyo" + +from SCons.Builder import Builder +import SCons.Util + +cmd = "cd ${SOURCE.dir} && $RPCGEN -%s $RPCGENFLAGS %s -o ${TARGET.abspath} ${SOURCE.file}" + +rpcgen_client = cmd % ('l', '$RPCGENCLIENTFLAGS') +rpcgen_header = cmd % ('h', '$RPCGENHEADERFLAGS') +rpcgen_service = cmd % ('m', '$RPCGENSERVICEFLAGS') +rpcgen_xdr = cmd % ('c', '$RPCGENXDRFLAGS') + +def generate(env): + "Add RPCGEN Builders and construction variables for an Environment." + + client = Builder(action=rpcgen_client, suffix='_clnt.c', src_suffix='.x') + header = Builder(action=rpcgen_header, suffix='.h', src_suffix='.x') + service = Builder(action=rpcgen_service, suffix='_svc.c', src_suffix='.x') + xdr = Builder(action=rpcgen_xdr, suffix='_xdr.c', src_suffix='.x') + env.Append(BUILDERS={'RPCGenClient' : client, + 'RPCGenHeader' : header, + 'RPCGenService' : service, + 'RPCGenXDR' : xdr}) + env['RPCGEN'] = 'rpcgen' + env['RPCGENFLAGS'] = SCons.Util.CLVar('') + env['RPCGENCLIENTFLAGS'] = SCons.Util.CLVar('') + env['RPCGENHEADERFLAGS'] = SCons.Util.CLVar('') + env['RPCGENSERVICEFLAGS'] = SCons.Util.CLVar('') + env['RPCGENXDRFLAGS'] = SCons.Util.CLVar('') + +def exists(env): + return env.Detect('rpcgen') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/rpm.py b/scons/scons-local-2.3.0/SCons/Tool/rpm.py new file mode 100644 index 000000000..ef31bd574 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/rpm.py @@ -0,0 +1,132 @@ +"""SCons.Tool.rpm + +Tool-specific initialization for rpm. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +The rpm tool calls the rpmbuild command. The first and only argument should a +tar.gz consisting of the source file and a specfile. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/rpm.py 2013/03/03 09:48:35 garyo" + +import os +import re +import shutil +import subprocess + +import SCons.Builder +import SCons.Node.FS +import SCons.Util +import SCons.Action +import SCons.Defaults + +def get_cmd(source, env): + tar_file_with_included_specfile = source + if SCons.Util.is_List(source): + tar_file_with_included_specfile = source[0] + return "%s %s %s"%(env['RPM'], env['RPMFLAGS'], + tar_file_with_included_specfile.abspath ) + +def build_rpm(target, source, env): + # create a temporary rpm build root. + tmpdir = os.path.join( os.path.dirname( target[0].abspath ), 'rpmtemp' ) + if os.path.exists(tmpdir): + shutil.rmtree(tmpdir) + + # now create the mandatory rpm directory structure. + for d in ['RPMS', 'SRPMS', 'SPECS', 'BUILD']: + os.makedirs( os.path.join( tmpdir, d ) ) + + # set the topdir as an rpmflag. + env.Prepend( RPMFLAGS = '--define \'_topdir %s\'' % tmpdir ) + + # now call rpmbuild to create the rpm package. + handle = subprocess.Popen(get_cmd(source, env), + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + shell=True) + output = handle.stdout.read() + status = handle.wait() + + if status: + raise SCons.Errors.BuildError( node=target[0], + errstr=output, + filename=str(target[0]) ) + else: + # XXX: assume that LC_ALL=c is set while running rpmbuild + output_files = re.compile( 'Wrote: (.*)' ).findall( output ) + + for output, input in zip( output_files, target ): + rpm_output = os.path.basename(output) + expected = os.path.basename(input.get_path()) + + assert expected == rpm_output, "got %s but expected %s" % (rpm_output, expected) + shutil.copy( output, input.abspath ) + + + # cleanup before leaving. + shutil.rmtree(tmpdir) + + return status + +def string_rpm(target, source, env): + try: + return env['RPMCOMSTR'] + except KeyError: + return get_cmd(source, env) + +rpmAction = SCons.Action.Action(build_rpm, string_rpm) + +RpmBuilder = SCons.Builder.Builder(action = SCons.Action.Action('$RPMCOM', '$RPMCOMSTR'), + source_scanner = SCons.Defaults.DirScanner, + suffix = '$RPMSUFFIX') + + + +def generate(env): + """Add Builders and construction variables for rpm to an Environment.""" + try: + bld = env['BUILDERS']['Rpm'] + except KeyError: + bld = RpmBuilder + env['BUILDERS']['Rpm'] = bld + + env.SetDefault(RPM = 'LC_ALL=c rpmbuild') + env.SetDefault(RPMFLAGS = SCons.Util.CLVar('-ta')) + env.SetDefault(RPMCOM = rpmAction) + env.SetDefault(RPMSUFFIX = '.rpm') + +def exists(env): + return env.Detect('rpmbuild') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/rpmutils.py b/scons/scons-local-2.3.0/SCons/Tool/rpmutils.py new file mode 100644 index 000000000..16d980a4f --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/rpmutils.py @@ -0,0 +1,533 @@ +"""SCons.Tool.rpmutils.py + +RPM specific helper routines for general usage in the test framework +and SCons core modules. + +Since we check for the RPM package target name in several places, +we have to know which machine/system name RPM will use for the current +hardware setup. The following dictionaries and functions try to +mimic the exact naming rules of the RPM source code. +They were directly derived from the file "rpmrc.in" of the version +rpm-4.9.1.3. For updating to a more recent version of RPM, this Python +script can be used standalone. The usage() function below shows the +exact syntax. + +""" + +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Tool/rpmutils.py 2013/03/03 09:48:35 garyo" + + +import platform + +# Start of rpmrc dictionaries (Marker, don't change or remove!) +os_canon = { + 'AIX' : ['AIX','5'], + 'AmigaOS' : ['AmigaOS','5'], + 'BSD_OS' : ['bsdi','12'], + 'CYGWIN32_95' : ['cygwin32','15'], + 'CYGWIN32_NT' : ['cygwin32','14'], + 'Darwin' : ['darwin','21'], + 'FreeBSD' : ['FreeBSD','8'], + 'HP-UX' : ['hpux10','6'], + 'IRIX' : ['Irix','2'], + 'IRIX64' : ['Irix64','10'], + 'Linux' : ['Linux','1'], + 'Linux/390' : ['OS/390','20'], + 'Linux/ESA' : ['VM/ESA','20'], + 'MacOSX' : ['macosx','21'], + 'MiNT' : ['FreeMiNT','17'], + 'NEXTSTEP' : ['NextStep','11'], + 'OS/390' : ['OS/390','18'], + 'OSF1' : ['osf1','7'], + 'SCO_SV' : ['SCO_SV3.2v5.0.2','9'], + 'SunOS4' : ['SunOS','4'], + 'SunOS5' : ['solaris','3'], + 'UNIX_SV' : ['MP_RAS','16'], + 'VM/ESA' : ['VM/ESA','19'], + 'machten' : ['machten','13'], + 'osf3.2' : ['osf1','7'], + 'osf4.0' : ['osf1','7'], +} + +buildarch_compat = { + 'alpha' : ['noarch'], + 'alphaev5' : ['alpha'], + 'alphaev56' : ['alphaev5'], + 'alphaev6' : ['alphapca56'], + 'alphaev67' : ['alphaev6'], + 'alphapca56' : ['alphaev56'], + 'amd64' : ['x86_64'], + 'armv3l' : ['noarch'], + 'armv4b' : ['noarch'], + 'armv4l' : ['armv3l'], + 'armv4tl' : ['armv4l'], + 'armv5tejl' : ['armv5tel'], + 'armv5tel' : ['armv4tl'], + 'armv6l' : ['armv5tejl'], + 'armv7l' : ['armv6l'], + 'atariclone' : ['m68kmint','noarch'], + 'atarist' : ['m68kmint','noarch'], + 'atariste' : ['m68kmint','noarch'], + 'ataritt' : ['m68kmint','noarch'], + 'athlon' : ['i686'], + 'falcon' : ['m68kmint','noarch'], + 'geode' : ['i586'], + 'hades' : ['m68kmint','noarch'], + 'hppa1.0' : ['parisc'], + 'hppa1.1' : ['hppa1.0'], + 'hppa1.2' : ['hppa1.1'], + 'hppa2.0' : ['hppa1.2'], + 'i386' : ['noarch','fat'], + 'i486' : ['i386'], + 'i586' : ['i486'], + 'i686' : ['i586'], + 'ia32e' : ['x86_64'], + 'ia64' : ['noarch'], + 'm68k' : ['noarch'], + 'milan' : ['m68kmint','noarch'], + 'mips' : ['noarch'], + 'mipsel' : ['noarch'], + 'parisc' : ['noarch'], + 'pentium3' : ['i686'], + 'pentium4' : ['pentium3'], + 'ppc' : ['noarch','fat'], + 'ppc32dy4' : ['noarch'], + 'ppc64' : ['noarch','fat'], + 'ppc64iseries' : ['ppc64'], + 'ppc64pseries' : ['ppc64'], + 'ppc8260' : ['noarch'], + 'ppc8560' : ['noarch'], + 'ppciseries' : ['noarch'], + 'ppcpseries' : ['noarch'], + 's390' : ['noarch'], + 's390x' : ['noarch'], + 'sh3' : ['noarch'], + 'sh4' : ['noarch'], + 'sh4a' : ['sh4'], + 'sparc' : ['noarch'], + 'sparc64' : ['sparcv9v'], + 'sparc64v' : ['sparc64'], + 'sparcv8' : ['sparc'], + 'sparcv9' : ['sparcv8'], + 'sparcv9v' : ['sparcv9'], + 'sun4c' : ['noarch'], + 'sun4d' : ['noarch'], + 'sun4m' : ['noarch'], + 'sun4u' : ['noarch'], + 'x86_64' : ['noarch'], +} + +os_compat = { + 'BSD_OS' : ['bsdi'], + 'Darwin' : ['MacOSX'], + 'FreeMiNT' : ['mint','MiNT','TOS'], + 'IRIX64' : ['IRIX'], + 'MiNT' : ['FreeMiNT','mint','TOS'], + 'TOS' : ['FreeMiNT','MiNT','mint'], + 'bsdi4.0' : ['bsdi'], + 'hpux10.00' : ['hpux9.07'], + 'hpux10.01' : ['hpux10.00'], + 'hpux10.10' : ['hpux10.01'], + 'hpux10.20' : ['hpux10.10'], + 'hpux10.30' : ['hpux10.20'], + 'hpux11.00' : ['hpux10.30'], + 'hpux9.05' : ['hpux9.04'], + 'hpux9.07' : ['hpux9.05'], + 'mint' : ['FreeMiNT','MiNT','TOS'], + 'ncr-sysv4.3' : ['ncr-sysv4.2'], + 'osf4.0' : ['osf3.2','osf1'], + 'solaris2.4' : ['solaris2.3'], + 'solaris2.5' : ['solaris2.3','solaris2.4'], + 'solaris2.6' : ['solaris2.3','solaris2.4','solaris2.5'], + 'solaris2.7' : ['solaris2.3','solaris2.4','solaris2.5','solaris2.6'], +} + +arch_compat = { + 'alpha' : ['axp','noarch'], + 'alphaev5' : ['alpha'], + 'alphaev56' : ['alphaev5'], + 'alphaev6' : ['alphapca56'], + 'alphaev67' : ['alphaev6'], + 'alphapca56' : ['alphaev56'], + 'amd64' : ['x86_64','athlon','noarch'], + 'armv3l' : ['noarch'], + 'armv4b' : ['noarch'], + 'armv4l' : ['armv3l'], + 'armv4tl' : ['armv4l'], + 'armv5tejl' : ['armv5tel'], + 'armv5tel' : ['armv4tl'], + 'armv6l' : ['armv5tejl'], + 'armv7l' : ['armv6l'], + 'atariclone' : ['m68kmint','noarch'], + 'atarist' : ['m68kmint','noarch'], + 'atariste' : ['m68kmint','noarch'], + 'ataritt' : ['m68kmint','noarch'], + 'athlon' : ['i686'], + 'falcon' : ['m68kmint','noarch'], + 'geode' : ['i586'], + 'hades' : ['m68kmint','noarch'], + 'hppa1.0' : ['parisc'], + 'hppa1.1' : ['hppa1.0'], + 'hppa1.2' : ['hppa1.1'], + 'hppa2.0' : ['hppa1.2'], + 'i370' : ['noarch'], + 'i386' : ['noarch','fat'], + 'i486' : ['i386'], + 'i586' : ['i486'], + 'i686' : ['i586'], + 'ia32e' : ['x86_64','athlon','noarch'], + 'ia64' : ['noarch'], + 'milan' : ['m68kmint','noarch'], + 'mips' : ['noarch'], + 'mipsel' : ['noarch'], + 'osfmach3_i386' : ['i486'], + 'osfmach3_i486' : ['i486','osfmach3_i386'], + 'osfmach3_i586' : ['i586','osfmach3_i486'], + 'osfmach3_i686' : ['i686','osfmach3_i586'], + 'osfmach3_ppc' : ['ppc'], + 'parisc' : ['noarch'], + 'pentium3' : ['i686'], + 'pentium4' : ['pentium3'], + 'powerpc' : ['ppc'], + 'powerppc' : ['ppc'], + 'ppc' : ['rs6000'], + 'ppc32dy4' : ['ppc'], + 'ppc64' : ['ppc'], + 'ppc64iseries' : ['ppc64'], + 'ppc64pseries' : ['ppc64'], + 'ppc8260' : ['ppc'], + 'ppc8560' : ['ppc'], + 'ppciseries' : ['ppc'], + 'ppcpseries' : ['ppc'], + 'rs6000' : ['noarch','fat'], + 's390' : ['noarch'], + 's390x' : ['s390','noarch'], + 'sh3' : ['noarch'], + 'sh4' : ['noarch'], + 'sh4a' : ['sh4'], + 'sparc' : ['noarch'], + 'sparc64' : ['sparcv9'], + 'sparc64v' : ['sparc64'], + 'sparcv8' : ['sparc'], + 'sparcv9' : ['sparcv8'], + 'sparcv9v' : ['sparcv9'], + 'sun4c' : ['sparc'], + 'sun4d' : ['sparc'], + 'sun4m' : ['sparc'], + 'sun4u' : ['sparc64'], + 'x86_64' : ['amd64','athlon','noarch'], +} + +buildarchtranslate = { + 'alphaev5' : ['alpha'], + 'alphaev56' : ['alpha'], + 'alphaev6' : ['alpha'], + 'alphaev67' : ['alpha'], + 'alphapca56' : ['alpha'], + 'amd64' : ['x86_64'], + 'armv3l' : ['armv3l'], + 'armv4b' : ['armv4b'], + 'armv4l' : ['armv4l'], + 'armv4tl' : ['armv4tl'], + 'armv5tejl' : ['armv5tejl'], + 'armv5tel' : ['armv5tel'], + 'armv6l' : ['armv6l'], + 'armv7l' : ['armv7l'], + 'atariclone' : ['m68kmint'], + 'atarist' : ['m68kmint'], + 'atariste' : ['m68kmint'], + 'ataritt' : ['m68kmint'], + 'athlon' : ['i386'], + 'falcon' : ['m68kmint'], + 'geode' : ['i386'], + 'hades' : ['m68kmint'], + 'i386' : ['i386'], + 'i486' : ['i386'], + 'i586' : ['i386'], + 'i686' : ['i386'], + 'ia32e' : ['x86_64'], + 'ia64' : ['ia64'], + 'milan' : ['m68kmint'], + 'osfmach3_i386' : ['i386'], + 'osfmach3_i486' : ['i386'], + 'osfmach3_i586' : ['i386'], + 'osfmach3_i686' : ['i386'], + 'osfmach3_ppc' : ['ppc'], + 'pentium3' : ['i386'], + 'pentium4' : ['i386'], + 'powerpc' : ['ppc'], + 'powerppc' : ['ppc'], + 'ppc32dy4' : ['ppc'], + 'ppc64iseries' : ['ppc64'], + 'ppc64pseries' : ['ppc64'], + 'ppc8260' : ['ppc'], + 'ppc8560' : ['ppc'], + 'ppciseries' : ['ppc'], + 'ppcpseries' : ['ppc'], + 's390' : ['s390'], + 's390x' : ['s390x'], + 'sh3' : ['sh3'], + 'sh4' : ['sh4'], + 'sh4a' : ['sh4'], + 'sparc64v' : ['sparc64'], + 'sparcv8' : ['sparc'], + 'sparcv9' : ['sparc'], + 'sparcv9v' : ['sparc'], + 'sun4c' : ['sparc'], + 'sun4d' : ['sparc'], + 'sun4m' : ['sparc'], + 'sun4u' : ['sparc64'], + 'x86_64' : ['x86_64'], +} + +optflags = { + 'alpha' : ['-O2','-g','-mieee'], + 'alphaev5' : ['-O2','-g','-mieee','-mtune=ev5'], + 'alphaev56' : ['-O2','-g','-mieee','-mtune=ev56'], + 'alphaev6' : ['-O2','-g','-mieee','-mtune=ev6'], + 'alphaev67' : ['-O2','-g','-mieee','-mtune=ev67'], + 'alphapca56' : ['-O2','-g','-mieee','-mtune=pca56'], + 'amd64' : ['-O2','-g'], + 'armv3l' : ['-O2','-g','-march=armv3'], + 'armv4b' : ['-O2','-g','-march=armv4'], + 'armv4l' : ['-O2','-g','-march=armv4'], + 'armv4tl' : ['-O2','-g','-march=armv4t'], + 'armv5tejl' : ['-O2','-g','-march=armv5te'], + 'armv5tel' : ['-O2','-g','-march=armv5te'], + 'armv6l' : ['-O2','-g','-march=armv6'], + 'armv7l' : ['-O2','-g','-march=armv7'], + 'atariclone' : ['-O2','-g','-fomit-frame-pointer'], + 'atarist' : ['-O2','-g','-fomit-frame-pointer'], + 'atariste' : ['-O2','-g','-fomit-frame-pointer'], + 'ataritt' : ['-O2','-g','-fomit-frame-pointer'], + 'athlon' : ['-O2','-g','-march=athlon'], + 'falcon' : ['-O2','-g','-fomit-frame-pointer'], + 'fat' : ['-O2','-g','-arch','i386','-arch','ppc'], + 'geode' : ['-Os','-g','-m32','-march=geode'], + 'hades' : ['-O2','-g','-fomit-frame-pointer'], + 'hppa1.0' : ['-O2','-g','-mpa-risc-1-0'], + 'hppa1.1' : ['-O2','-g','-mpa-risc-1-0'], + 'hppa1.2' : ['-O2','-g','-mpa-risc-1-0'], + 'hppa2.0' : ['-O2','-g','-mpa-risc-1-0'], + 'i386' : ['-O2','-g','-march=i386','-mtune=i686'], + 'i486' : ['-O2','-g','-march=i486'], + 'i586' : ['-O2','-g','-march=i586'], + 'i686' : ['-O2','-g','-march=i686'], + 'ia32e' : ['-O2','-g'], + 'ia64' : ['-O2','-g'], + 'm68k' : ['-O2','-g','-fomit-frame-pointer'], + 'milan' : ['-O2','-g','-fomit-frame-pointer'], + 'mips' : ['-O2','-g'], + 'mipsel' : ['-O2','-g'], + 'parisc' : ['-O2','-g','-mpa-risc-1-0'], + 'pentium3' : ['-O2','-g','-march=pentium3'], + 'pentium4' : ['-O2','-g','-march=pentium4'], + 'ppc' : ['-O2','-g','-fsigned-char'], + 'ppc32dy4' : ['-O2','-g','-fsigned-char'], + 'ppc64' : ['-O2','-g','-fsigned-char'], + 'ppc8260' : ['-O2','-g','-fsigned-char'], + 'ppc8560' : ['-O2','-g','-fsigned-char'], + 'ppciseries' : ['-O2','-g','-fsigned-char'], + 'ppcpseries' : ['-O2','-g','-fsigned-char'], + 's390' : ['-O2','-g'], + 's390x' : ['-O2','-g'], + 'sh3' : ['-O2','-g'], + 'sh4' : ['-O2','-g','-mieee'], + 'sh4a' : ['-O2','-g','-mieee'], + 'sparc' : ['-O2','-g','-m32','-mtune=ultrasparc'], + 'sparc64' : ['-O2','-g','-m64','-mtune=ultrasparc'], + 'sparc64v' : ['-O2','-g','-m64','-mtune=niagara'], + 'sparcv8' : ['-O2','-g','-m32','-mtune=ultrasparc','-mv8'], + 'sparcv9' : ['-O2','-g','-m32','-mtune=ultrasparc'], + 'sparcv9v' : ['-O2','-g','-m32','-mtune=niagara'], + 'x86_64' : ['-O2','-g'], +} + +arch_canon = { + 'IP' : ['sgi','7'], + 'alpha' : ['alpha','2'], + 'alphaev5' : ['alphaev5','2'], + 'alphaev56' : ['alphaev56','2'], + 'alphaev6' : ['alphaev6','2'], + 'alphaev67' : ['alphaev67','2'], + 'alphapca56' : ['alphapca56','2'], + 'amd64' : ['amd64','1'], + 'armv3l' : ['armv3l','12'], + 'armv4b' : ['armv4b','12'], + 'armv4l' : ['armv4l','12'], + 'armv5tejl' : ['armv5tejl','12'], + 'armv5tel' : ['armv5tel','12'], + 'armv6l' : ['armv6l','12'], + 'armv7l' : ['armv7l','12'], + 'atariclone' : ['m68kmint','13'], + 'atarist' : ['m68kmint','13'], + 'atariste' : ['m68kmint','13'], + 'ataritt' : ['m68kmint','13'], + 'athlon' : ['athlon','1'], + 'falcon' : ['m68kmint','13'], + 'geode' : ['geode','1'], + 'hades' : ['m68kmint','13'], + 'i370' : ['i370','14'], + 'i386' : ['i386','1'], + 'i486' : ['i486','1'], + 'i586' : ['i586','1'], + 'i686' : ['i686','1'], + 'ia32e' : ['ia32e','1'], + 'ia64' : ['ia64','9'], + 'm68k' : ['m68k','6'], + 'm68kmint' : ['m68kmint','13'], + 'milan' : ['m68kmint','13'], + 'mips' : ['mips','4'], + 'mipsel' : ['mipsel','11'], + 'pentium3' : ['pentium3','1'], + 'pentium4' : ['pentium4','1'], + 'ppc' : ['ppc','5'], + 'ppc32dy4' : ['ppc32dy4','5'], + 'ppc64' : ['ppc64','16'], + 'ppc64iseries' : ['ppc64iseries','16'], + 'ppc64pseries' : ['ppc64pseries','16'], + 'ppc8260' : ['ppc8260','5'], + 'ppc8560' : ['ppc8560','5'], + 'ppciseries' : ['ppciseries','5'], + 'ppcpseries' : ['ppcpseries','5'], + 'rs6000' : ['rs6000','8'], + 's390' : ['s390','14'], + 's390x' : ['s390x','15'], + 'sh' : ['sh','17'], + 'sh3' : ['sh3','17'], + 'sh4' : ['sh4','17'], + 'sh4a' : ['sh4a','17'], + 'sparc' : ['sparc','3'], + 'sparc64' : ['sparc64','2'], + 'sparc64v' : ['sparc64v','2'], + 'sparcv8' : ['sparcv8','3'], + 'sparcv9' : ['sparcv9','3'], + 'sparcv9v' : ['sparcv9v','3'], + 'sun4' : ['sparc','3'], + 'sun4c' : ['sparc','3'], + 'sun4d' : ['sparc','3'], + 'sun4m' : ['sparc','3'], + 'sun4u' : ['sparc64','2'], + 'x86_64' : ['x86_64','1'], + 'xtensa' : ['xtensa','18'], +} + +# End of rpmrc dictionaries (Marker, don't change or remove!) + +def defaultMachine(): + """ Return the canonicalized machine name. """ + rmachine = platform.machine() + + # Try to lookup the string in the canon table + if rmachine in arch_canon: + rmachine = arch_canon[rmachine][0] + + return rmachine + +def defaultSystem(): + """ Return the canonicalized system name. """ + rsystem = platform.system() + + # Try to lookup the string in the canon tables + if rsystem in os_canon: + rsystem = os_canon[rsystem][0] + + return rsystem + +def defaultNames(): + """ Return the canonicalized machine and system name. """ + return defaultMachine(), defaultSystem() + +def updateRpmDicts(rpmrc, pyfile): + """ Read the given rpmrc file with RPM definitions and update the + info dictionaries in the file pyfile with it. + The arguments will usually be 'rpmrc.in' from a recent RPM source + tree, and 'rpmutils.py' referring to this script itself. + See also usage() below. + """ + try: + # Read old rpmutils.py file + oldpy = open(pyfile,"r").readlines() + # Read current rpmrc.in file + rpm = open(rpmrc,"r").readlines() + # Parse for data + data = {} + # Allowed section names that get parsed + sections = ['optflags', + 'arch_canon', + 'os_canon', + 'buildarchtranslate', + 'arch_compat', + 'os_compat', + 'buildarch_compat'] + for l in rpm: + l = l.rstrip('\n').replace(':',' ') + # Skip comments + if l.lstrip().startswith('#'): + continue + tokens = l.strip().split() + if len(tokens): + key = tokens[0] + if key in sections: + # Have we met this section before? + if not data.has_key(tokens[0]): + # No, so insert it + data[key] = {} + # Insert data + data[key][tokens[1]] = tokens[2:] + # Write new rpmutils.py file + out = open(pyfile,"w") + pm = 0 + for l in oldpy: + if pm: + if l.startswith('# End of rpmrc dictionaries'): + pm = 0 + out.write(l) + else: + out.write(l) + if l.startswith('# Start of rpmrc dictionaries'): + pm = 1 + # Write data sections to single dictionaries + for key, entries in data.iteritems(): + out.write("%s = {\n" % key) + for arch in sorted(entries.keys()): + out.write(" '%s' : ['%s'],\n" % (arch, "','".join(entries[arch]))) + out.write("}\n\n") + out.close() + except: + pass + +def usage(): + print "rpmutils.py rpmrc.in rpmutils.py" + +def main(): + import sys + + if len(sys.argv) < 3: + usage() + sys.exit(0) + updateRpmDicts(sys.argv[1], sys.argv[2]) + +if __name__ == "__main__": + main() diff --git a/scons/scons-local-2.3.0/SCons/Tool/sgiar.py b/scons/scons-local-2.3.0/SCons/Tool/sgiar.py new file mode 100644 index 000000000..27f5f7544 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/sgiar.py @@ -0,0 +1,68 @@ +"""SCons.Tool.sgiar + +Tool-specific initialization for SGI ar (library archive). If CC +exists, static libraries should be built with it, so the prelinker has +a chance to resolve C++ template instantiations. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/sgiar.py 2013/03/03 09:48:35 garyo" + +import SCons.Defaults +import SCons.Tool +import SCons.Util + +def generate(env): + """Add Builders and construction variables for ar to an Environment.""" + SCons.Tool.createStaticLibBuilder(env) + + if env.Detect('CC'): + env['AR'] = 'CC' + env['ARFLAGS'] = SCons.Util.CLVar('-ar') + env['ARCOM'] = '$AR $ARFLAGS -o $TARGET $SOURCES' + else: + env['AR'] = 'ar' + env['ARFLAGS'] = SCons.Util.CLVar('r') + env['ARCOM'] = '$AR $ARFLAGS $TARGET $SOURCES' + + env['SHLINK'] = '$LINK' + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') + env['SHLINKCOM'] = '$SHLINK $SHLINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' + env['LIBPREFIX'] = 'lib' + env['LIBSUFFIX'] = '.a' + +def exists(env): + return env.Detect('CC') or env.Detect('ar') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/sgic++.py b/scons/scons-local-2.3.0/SCons/Tool/sgic++.py new file mode 100644 index 000000000..ae2ac2c4b --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/sgic++.py @@ -0,0 +1,58 @@ +"""SCons.Tool.sgic++ + +Tool-specific initialization for MIPSpro C++ on SGI. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/sgic++.py 2013/03/03 09:48:35 garyo" + +import SCons.Util + +cplusplus = __import__('c++', globals(), locals(), []) + +def generate(env): + """Add Builders and construction variables for SGI MIPS C++ to an Environment.""" + + cplusplus.generate(env) + + env['CXX'] = 'CC' + env['CXXFLAGS'] = SCons.Util.CLVar('-LANG:std') + env['SHCXX'] = '$CXX' + env['SHOBJSUFFIX'] = '.o' + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 + +def exists(env): + return env.Detect('CC') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/sgicc.py b/scons/scons-local-2.3.0/SCons/Tool/sgicc.py new file mode 100644 index 000000000..54e05a31b --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/sgicc.py @@ -0,0 +1,53 @@ +"""SCons.Tool.sgicc + +Tool-specific initialization for MIPSPro cc on SGI. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/sgicc.py 2013/03/03 09:48:35 garyo" + +import cc + +def generate(env): + """Add Builders and construction variables for gcc to an Environment.""" + cc.generate(env) + + env['CXX'] = 'CC' + env['SHOBJSUFFIX'] = '.o' + env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 + +def exists(env): + return env.Detect('cc') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/sgilink.py b/scons/scons-local-2.3.0/SCons/Tool/sgilink.py new file mode 100644 index 000000000..82c44193e --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/sgilink.py @@ -0,0 +1,62 @@ +"""SCons.Tool.sgilink + +Tool-specific initialization for the SGI MIPSPro linker on SGI. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/sgilink.py 2013/03/03 09:48:35 garyo" + +import SCons.Util + +import link + +linkers = ['CC', 'cc'] + +def generate(env): + """Add Builders and construction variables for MIPSPro to an Environment.""" + link.generate(env) + + env['LINK'] = env.Detect(linkers) or 'cc' + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared') + + # __RPATH is set to $_RPATH in the platform specification if that + # platform supports it. + env['RPATHPREFIX'] = '-rpath ' + env['RPATHSUFFIX'] = '' + env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}' + +def exists(env): + return env.Detect(linkers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/sunar.py b/scons/scons-local-2.3.0/SCons/Tool/sunar.py new file mode 100644 index 000000000..3a4d7c2dc --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/sunar.py @@ -0,0 +1,67 @@ +"""engine.SCons.Tool.sunar + +Tool-specific initialization for Solaris (Forte) ar (library archive). If CC +exists, static libraries should be built with it, so that template +instantians can be resolved. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/sunar.py 2013/03/03 09:48:35 garyo" + +import SCons.Defaults +import SCons.Tool +import SCons.Util + +def generate(env): + """Add Builders and construction variables for ar to an Environment.""" + SCons.Tool.createStaticLibBuilder(env) + + if env.Detect('CC'): + env['AR'] = 'CC' + env['ARFLAGS'] = SCons.Util.CLVar('-xar') + env['ARCOM'] = '$AR $ARFLAGS -o $TARGET $SOURCES' + else: + env['AR'] = 'ar' + env['ARFLAGS'] = SCons.Util.CLVar('r') + env['ARCOM'] = '$AR $ARFLAGS $TARGET $SOURCES' + + env['SHLINK'] = '$LINK' + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -G') + env['SHLINKCOM'] = '$SHLINK $SHLINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' + env['LIBPREFIX'] = 'lib' + env['LIBSUFFIX'] = '.a' + +def exists(env): + return env.Detect('CC') or env.Detect('ar') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/sunc++.py b/scons/scons-local-2.3.0/SCons/Tool/sunc++.py new file mode 100644 index 000000000..7234239e0 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/sunc++.py @@ -0,0 +1,142 @@ +"""SCons.Tool.sunc++ + +Tool-specific initialization for C++ on SunOS / Solaris. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/sunc++.py 2013/03/03 09:48:35 garyo" + +import SCons + +import os +import re +import subprocess + +cplusplus = __import__('c++', globals(), locals(), []) + +package_info = {} + +def get_package_info(package_name, pkginfo, pkgchk): + try: + return package_info[package_name] + except KeyError: + version = None + pathname = None + try: + sadm_contents = open('/var/sadm/install/contents', 'r').read() + except EnvironmentError: + pass + else: + sadm_re = re.compile('^(\S*/bin/CC)(=\S*)? %s$' % package_name, re.M) + sadm_match = sadm_re.search(sadm_contents) + if sadm_match: + pathname = os.path.dirname(sadm_match.group(1)) + + try: + p = subprocess.Popen([pkginfo, '-l', package_name], + stdout=subprocess.PIPE, + stderr=open('/dev/null', 'w')) + except EnvironmentError: + pass + else: + pkginfo_contents = p.communicate()[0] + version_re = re.compile('^ *VERSION:\s*(.*)$', re.M) + version_match = version_re.search(pkginfo_contents) + if version_match: + version = version_match.group(1) + + if pathname is None: + try: + p = subprocess.Popen([pkgchk, '-l', package_name], + stdout=subprocess.PIPE, + stderr=open('/dev/null', 'w')) + except EnvironmentError: + pass + else: + pkgchk_contents = p.communicate()[0] + pathname_re = re.compile(r'^Pathname:\s*(.*/bin/CC)$', re.M) + pathname_match = pathname_re.search(pkgchk_contents) + if pathname_match: + pathname = os.path.dirname(pathname_match.group(1)) + + package_info[package_name] = (pathname, version) + return package_info[package_name] + +# use the package installer tool lslpp to figure out where cppc and what +# version of it is installed +def get_cppc(env): + cxx = env.subst('$CXX') + if cxx: + cppcPath = os.path.dirname(cxx) + else: + cppcPath = None + + cppcVersion = None + + pkginfo = env.subst('$PKGINFO') + pkgchk = env.subst('$PKGCHK') + + for package in ['SPROcpl']: + path, version = get_package_info(package, pkginfo, pkgchk) + if path and version: + cppcPath, cppcVersion = path, version + break + + return (cppcPath, 'CC', 'CC', cppcVersion) + +def generate(env): + """Add Builders and construction variables for SunPRO C++.""" + path, cxx, shcxx, version = get_cppc(env) + if path: + cxx = os.path.join(path, cxx) + shcxx = os.path.join(path, shcxx) + + cplusplus.generate(env) + + env['CXX'] = cxx + env['SHCXX'] = shcxx + env['CXXVERSION'] = version + env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS -KPIC') + env['SHOBJPREFIX'] = 'so_' + env['SHOBJSUFFIX'] = '.o' + +def exists(env): + path, cxx, shcxx, version = get_cppc(env) + if path and cxx: + cppc = os.path.join(path, cxx) + if os.path.exists(cppc): + return cppc + return None + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/suncc.py b/scons/scons-local-2.3.0/SCons/Tool/suncc.py new file mode 100644 index 000000000..f7442d8bd --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/suncc.py @@ -0,0 +1,58 @@ +"""SCons.Tool.suncc + +Tool-specific initialization for Sun Solaris (Forte) CC and cc. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/suncc.py 2013/03/03 09:48:35 garyo" + +import SCons.Util + +import cc + +def generate(env): + """ + Add Builders and construction variables for Forte C and C++ compilers + to an Environment. + """ + cc.generate(env) + + env['CXX'] = 'CC' + env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS -KPIC') + env['SHOBJPREFIX'] = 'so_' + env['SHOBJSUFFIX'] = '.o' + +def exists(env): + return env.Detect('CC') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/sunf77.py b/scons/scons-local-2.3.0/SCons/Tool/sunf77.py new file mode 100644 index 000000000..eb6378156 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/sunf77.py @@ -0,0 +1,63 @@ +"""SCons.Tool.sunf77 + +Tool-specific initialization for sunf77, the Sun Studio F77 compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/sunf77.py 2013/03/03 09:48:35 garyo" + +import SCons.Util + +from FortranCommon import add_all_to_env + +compilers = ['sunf77', 'f77'] + +def generate(env): + """Add Builders and construction variables for sunf77 to an Environment.""" + add_all_to_env(env) + + fcomp = env.Detect(compilers) or 'f77' + env['FORTRAN'] = fcomp + env['F77'] = fcomp + + env['SHFORTRAN'] = '$FORTRAN' + env['SHF77'] = '$F77' + + env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -KPIC') + env['SHF77FLAGS'] = SCons.Util.CLVar('$F77FLAGS -KPIC') + +def exists(env): + return env.Detect(compilers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/sunf90.py b/scons/scons-local-2.3.0/SCons/Tool/sunf90.py new file mode 100644 index 000000000..cfee2ee7c --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/sunf90.py @@ -0,0 +1,64 @@ +"""SCons.Tool.sunf90 + +Tool-specific initialization for sunf90, the Sun Studio F90 compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/sunf90.py 2013/03/03 09:48:35 garyo" + +import SCons.Util + +from FortranCommon import add_all_to_env + +compilers = ['sunf90', 'f90'] + +def generate(env): + """Add Builders and construction variables for sun f90 compiler to an + Environment.""" + add_all_to_env(env) + + fcomp = env.Detect(compilers) or 'f90' + env['FORTRAN'] = fcomp + env['F90'] = fcomp + + env['SHFORTRAN'] = '$FORTRAN' + env['SHF90'] = '$F90' + + env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -KPIC') + env['SHF90FLAGS'] = SCons.Util.CLVar('$F90FLAGS -KPIC') + +def exists(env): + return env.Detect(compilers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/sunf95.py b/scons/scons-local-2.3.0/SCons/Tool/sunf95.py new file mode 100644 index 000000000..31d57b11e --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/sunf95.py @@ -0,0 +1,64 @@ +"""SCons.Tool.sunf95 + +Tool-specific initialization for sunf95, the Sun Studio F95 compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/sunf95.py 2013/03/03 09:48:35 garyo" + +import SCons.Util + +from FortranCommon import add_all_to_env + +compilers = ['sunf95', 'f95'] + +def generate(env): + """Add Builders and construction variables for sunf95 to an + Environment.""" + add_all_to_env(env) + + fcomp = env.Detect(compilers) or 'f95' + env['FORTRAN'] = fcomp + env['F95'] = fcomp + + env['SHFORTRAN'] = '$FORTRAN' + env['SHF95'] = '$F95' + + env['SHFORTRANFLAGS'] = SCons.Util.CLVar('$FORTRANFLAGS -KPIC') + env['SHF95FLAGS'] = SCons.Util.CLVar('$F95FLAGS -KPIC') + +def exists(env): + return env.Detect(compilers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/sunlink.py b/scons/scons-local-2.3.0/SCons/Tool/sunlink.py new file mode 100644 index 000000000..46393417e --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/sunlink.py @@ -0,0 +1,76 @@ +"""SCons.Tool.sunlink + +Tool-specific initialization for the Sun Solaris (Forte) linker. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/sunlink.py 2013/03/03 09:48:35 garyo" + +import os +import os.path + +import SCons.Util + +import link + +ccLinker = None + +# search for the acc compiler and linker front end + +try: + dirs = os.listdir('/opt') +except (IOError, OSError): + # Not being able to read the directory because it doesn't exist + # (IOError) or isn't readable (OSError) is okay. + dirs = [] + +for d in dirs: + linker = '/opt/' + d + '/bin/CC' + if os.path.exists(linker): + ccLinker = linker + break + +def generate(env): + """Add Builders and construction variables for Forte to an Environment.""" + link.generate(env) + + env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -G') + + env['RPATHPREFIX'] = '-R' + env['RPATHSUFFIX'] = '' + env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}' + +def exists(env): + return ccLinker + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/swig.py b/scons/scons-local-2.3.0/SCons/Tool/swig.py new file mode 100644 index 000000000..6bbe922cb --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/swig.py @@ -0,0 +1,183 @@ +"""SCons.Tool.swig + +Tool-specific initialization for swig. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/swig.py 2013/03/03 09:48:35 garyo" + +import os.path +import re +import subprocess + +import SCons.Action +import SCons.Defaults +import SCons.Scanner +import SCons.Tool +import SCons.Util + +SwigAction = SCons.Action.Action('$SWIGCOM', '$SWIGCOMSTR') + +def swigSuffixEmitter(env, source): + if '-c++' in SCons.Util.CLVar(env.subst("$SWIGFLAGS", source=source)): + return '$SWIGCXXFILESUFFIX' + else: + return '$SWIGCFILESUFFIX' + +# Match '%module test', as well as '%module(directors="1") test' +# Also allow for test to be quoted (SWIG permits double quotes, but not single) +# Also allow for the line to have spaces after test if not quoted +_reModule = re.compile(r'%module(\s*\(.*\))?\s+("?)(\S+)\2') + +def _find_modules(src): + """Find all modules referenced by %module lines in `src`, a SWIG .i file. + Returns a list of all modules, and a flag set if SWIG directors have + been requested (SWIG will generate an additional header file in this + case.)""" + directors = 0 + mnames = [] + try: + matches = _reModule.findall(open(src).read()) + except IOError: + # If the file's not yet generated, guess the module name from the file stem + matches = [] + mnames.append(os.path.splitext(os.path.basename(src))[0]) + + for m in matches: + mnames.append(m[2]) + directors = directors or m[0].find('directors') >= 0 + return mnames, directors + +def _add_director_header_targets(target, env): + # Directors only work with C++ code, not C + suffix = env.subst(env['SWIGCXXFILESUFFIX']) + # For each file ending in SWIGCXXFILESUFFIX, add a new target director + # header by replacing the ending with SWIGDIRECTORSUFFIX. + for x in target[:]: + n = x.name + d = x.dir + if n[-len(suffix):] == suffix: + target.append(d.File(n[:-len(suffix)] + env['SWIGDIRECTORSUFFIX'])) + +def _swigEmitter(target, source, env): + swigflags = env.subst("$SWIGFLAGS", target=target, source=source) + flags = SCons.Util.CLVar(swigflags) + for src in source: + src = str(src.rfile()) + mnames = None + if "-python" in flags and "-noproxy" not in flags: + if mnames is None: + mnames, directors = _find_modules(src) + if directors: + _add_director_header_targets(target, env) + python_files = [m + ".py" for m in mnames] + outdir = env.subst('$SWIGOUTDIR', target=target, source=source) + # .py files should be generated in SWIGOUTDIR if specified, + # otherwise in the same directory as the target + if outdir: + python_files = [env.fs.File(os.path.join(outdir, j)) for j in python_files] + else: + python_files = [target[0].dir.File(m) for m in python_files] + target.extend(python_files) + if "-java" in flags: + if mnames is None: + mnames, directors = _find_modules(src) + if directors: + _add_director_header_targets(target, env) + java_files = [[m + ".java", m + "JNI.java"] for m in mnames] + java_files = SCons.Util.flatten(java_files) + outdir = env.subst('$SWIGOUTDIR', target=target, source=source) + if outdir: + java_files = [os.path.join(outdir, j) for j in java_files] + java_files = list(map(env.fs.File, java_files)) + for jf in java_files: + t_from_s = lambda t, p, s, x: t.dir + SCons.Util.AddMethod(jf, t_from_s, 'target_from_source') + target.extend(java_files) + return (target, source) + +def _get_swig_version(env): + """Run the SWIG command line tool to get and return the version number""" + pipe = SCons.Action._subproc(env, [env['SWIG'], '-version'], + stdin = 'devnull', + stderr = 'devnull', + stdout = subprocess.PIPE) + if pipe.wait() != 0: return + + out = pipe.stdout.read() + match = re.search(r'SWIG Version\s+(\S+)$', out, re.MULTILINE) + if match: + return match.group(1) + +def generate(env): + """Add Builders and construction variables for swig to an Environment.""" + c_file, cxx_file = SCons.Tool.createCFileBuilders(env) + + c_file.suffix['.i'] = swigSuffixEmitter + cxx_file.suffix['.i'] = swigSuffixEmitter + + c_file.add_action('.i', SwigAction) + c_file.add_emitter('.i', _swigEmitter) + cxx_file.add_action('.i', SwigAction) + cxx_file.add_emitter('.i', _swigEmitter) + + java_file = SCons.Tool.CreateJavaFileBuilder(env) + + java_file.suffix['.i'] = swigSuffixEmitter + + java_file.add_action('.i', SwigAction) + java_file.add_emitter('.i', _swigEmitter) + + env['SWIG'] = 'swig' + env['SWIGVERSION'] = _get_swig_version(env) + env['SWIGFLAGS'] = SCons.Util.CLVar('') + env['SWIGDIRECTORSUFFIX'] = '_wrap.h' + env['SWIGCFILESUFFIX'] = '_wrap$CFILESUFFIX' + env['SWIGCXXFILESUFFIX'] = '_wrap$CXXFILESUFFIX' + env['_SWIGOUTDIR'] = r'${"-outdir \"%s\"" % SWIGOUTDIR}' + env['SWIGPATH'] = [] + env['SWIGINCPREFIX'] = '-I' + env['SWIGINCSUFFIX'] = '' + env['_SWIGINCFLAGS'] = '$( ${_concat(SWIGINCPREFIX, SWIGPATH, SWIGINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' + env['SWIGCOM'] = '$SWIG -o $TARGET ${_SWIGOUTDIR} ${_SWIGINCFLAGS} $SWIGFLAGS $SOURCES' + + expr = '^[ \t]*%[ \t]*(?:include|import|extern)[ \t]*(<|"?)([^>\s"]+)(?:>|"?)' + scanner = SCons.Scanner.ClassicCPP("SWIGScan", ".i", "SWIGPATH", expr) + + env.Append(SCANNERS = scanner) + +def exists(env): + return env.Detect(['swig']) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/tar.py b/scons/scons-local-2.3.0/SCons/Tool/tar.py new file mode 100644 index 000000000..e28935839 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/tar.py @@ -0,0 +1,73 @@ +"""SCons.Tool.tar + +Tool-specific initialization for tar. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/tar.py 2013/03/03 09:48:35 garyo" + +import SCons.Action +import SCons.Builder +import SCons.Defaults +import SCons.Node.FS +import SCons.Util + +tars = ['tar', 'gtar'] + +TarAction = SCons.Action.Action('$TARCOM', '$TARCOMSTR') + +TarBuilder = SCons.Builder.Builder(action = TarAction, + source_factory = SCons.Node.FS.Entry, + source_scanner = SCons.Defaults.DirScanner, + suffix = '$TARSUFFIX', + multi = 1) + + +def generate(env): + """Add Builders and construction variables for tar to an Environment.""" + try: + bld = env['BUILDERS']['Tar'] + except KeyError: + bld = TarBuilder + env['BUILDERS']['Tar'] = bld + + env['TAR'] = env.Detect(tars) or 'gtar' + env['TARFLAGS'] = SCons.Util.CLVar('-c') + env['TARCOM'] = '$TAR $TARFLAGS -f $TARGET $SOURCES' + env['TARSUFFIX'] = '.tar' + +def exists(env): + return env.Detect(tars) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/tex.py b/scons/scons-local-2.3.0/SCons/Tool/tex.py new file mode 100644 index 000000000..59606348c --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/tex.py @@ -0,0 +1,986 @@ +"""SCons.Tool.tex + +Tool-specific initialization for TeX. +Generates .dvi files from .tex files + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/tex.py 2013/03/03 09:48:35 garyo" + +import os.path +import re +import shutil +import sys +import platform +import glob + +import SCons.Action +import SCons.Node +import SCons.Node.FS +import SCons.Util +import SCons.Scanner.LaTeX + +Verbose = False + +must_rerun_latex = True + +# these are files that just need to be checked for changes and then rerun latex +check_suffixes = ['.toc', '.lof', '.lot', '.out', '.nav', '.snm'] + +# these are files that require bibtex or makeindex to be run when they change +all_suffixes = check_suffixes + ['.bbl', '.idx', '.nlo', '.glo', '.acn', '.bcf'] + +# +# regular expressions used to search for Latex features +# or outputs that require rerunning latex +# +# search for all .aux files opened by latex (recorded in the .fls file) +openout_aux_re = re.compile(r"OUTPUT *(.*\.aux)") + +# search for all .bcf files opened by latex (recorded in the .fls file) +# for use by biber +openout_bcf_re = re.compile(r"OUTPUT *(.*\.bcf)") + +#printindex_re = re.compile(r"^[^%]*\\printindex", re.MULTILINE) +#printnomenclature_re = re.compile(r"^[^%]*\\printnomenclature", re.MULTILINE) +#printglossary_re = re.compile(r"^[^%]*\\printglossary", re.MULTILINE) + +# search to find rerun warnings +warning_rerun_str = '(^LaTeX Warning:.*Rerun)|(^Package \w+ Warning:.*Rerun)' +warning_rerun_re = re.compile(warning_rerun_str, re.MULTILINE) + +# search to find citation rerun warnings +rerun_citations_str = "^LaTeX Warning:.*\n.*Rerun to get citations correct" +rerun_citations_re = re.compile(rerun_citations_str, re.MULTILINE) + +# search to find undefined references or citations warnings +undefined_references_str = '(^LaTeX Warning:.*undefined references)|(^Package \w+ Warning:.*undefined citations)' +undefined_references_re = re.compile(undefined_references_str, re.MULTILINE) + +# used by the emitter +auxfile_re = re.compile(r".", re.MULTILINE) +tableofcontents_re = re.compile(r"^[^%\n]*\\tableofcontents", re.MULTILINE) +makeindex_re = re.compile(r"^[^%\n]*\\makeindex", re.MULTILINE) +bibliography_re = re.compile(r"^[^%\n]*\\bibliography", re.MULTILINE) +bibunit_re = re.compile(r"^[^%\n]*\\begin\{bibunit\}", re.MULTILINE) +multibib_re = re.compile(r"^[^%\n]*\\newcites\{([^\}]*)\}", re.MULTILINE) +addbibresource_re = re.compile(r"^[^%\n]*\\(addbibresource|addglobalbib|addsectionbib)", re.MULTILINE) +listoffigures_re = re.compile(r"^[^%\n]*\\listoffigures", re.MULTILINE) +listoftables_re = re.compile(r"^[^%\n]*\\listoftables", re.MULTILINE) +hyperref_re = re.compile(r"^[^%\n]*\\usepackage.*\{hyperref\}", re.MULTILINE) +makenomenclature_re = re.compile(r"^[^%\n]*\\makenomenclature", re.MULTILINE) +makeglossary_re = re.compile(r"^[^%\n]*\\makeglossary", re.MULTILINE) +makeglossaries_re = re.compile(r"^[^%\n]*\\makeglossaries", re.MULTILINE) +makeacronyms_re = re.compile(r"^[^%\n]*\\makeglossaries", re.MULTILINE) +beamer_re = re.compile(r"^[^%\n]*\\documentclass\{beamer\}", re.MULTILINE) +regex = r'^[^%\n]*\\newglossary\s*\[([^\]]+)\]?\s*\{([^}]*)\}\s*\{([^}]*)\}\s*\{([^}]*)\}\s*\{([^}]*)\}' +newglossary_re = re.compile(regex, re.MULTILINE) + +newglossary_suffix = [] + +# search to find all files included by Latex +include_re = re.compile(r'^[^%\n]*\\(?:include|input){([^}]*)}', re.MULTILINE) +includeOnly_re = re.compile(r'^[^%\n]*\\(?:include){([^}]*)}', re.MULTILINE) + +# search to find all graphics files included by Latex +includegraphics_re = re.compile(r'^[^%\n]*\\(?:includegraphics(?:\[[^\]]+\])?){([^}]*)}', re.MULTILINE) + +# search to find all files opened by Latex (recorded in .log file) +openout_re = re.compile(r"OUTPUT *(.*)") + +# list of graphics file extensions for TeX and LaTeX +TexGraphics = SCons.Scanner.LaTeX.TexGraphics +LatexGraphics = SCons.Scanner.LaTeX.LatexGraphics + +# An Action sufficient to build any generic tex file. +TeXAction = None + +# An action to build a latex file. This action might be needed more +# than once if we are dealing with labels and bibtex. +LaTeXAction = None + +# An action to run BibTeX on a file. +BibTeXAction = None + +# An action to run Biber on a file. +BiberAction = None + +# An action to run MakeIndex on a file. +MakeIndexAction = None + +# An action to run MakeIndex (for nomencl) on a file. +MakeNclAction = None + +# An action to run MakeIndex (for glossary) on a file. +MakeGlossaryAction = None + +# An action to run MakeIndex (for acronyms) on a file. +MakeAcronymsAction = None + +# An action to run MakeIndex (for newglossary commands) on a file. +MakeNewGlossaryAction = None + +# Used as a return value of modify_env_var if the variable is not set. +_null = SCons.Scanner.LaTeX._null + +modify_env_var = SCons.Scanner.LaTeX.modify_env_var + +def check_file_error_message(utility, filename='log'): + msg = '%s returned an error, check the %s file\n' % (utility, filename) + sys.stdout.write(msg) + +def FindFile(name,suffixes,paths,env,requireExt=False): + if requireExt: + name,ext = SCons.Util.splitext(name) + # if the user gave an extension use it. + if ext: + name = name + ext + if Verbose: + print " searching for '%s' with extensions: " % name,suffixes + + for path in paths: + testName = os.path.join(path,name) + if Verbose: + print " look for '%s'" % testName + if os.path.isfile(testName): + if Verbose: + print " found '%s'" % testName + return env.fs.File(testName) + else: + name_ext = SCons.Util.splitext(testName)[1] + if name_ext: + continue + + # if no suffix try adding those passed in + for suffix in suffixes: + testNameExt = testName + suffix + if Verbose: + print " look for '%s'" % testNameExt + + if os.path.isfile(testNameExt): + if Verbose: + print " found '%s'" % testNameExt + return env.fs.File(testNameExt) + if Verbose: + print " did not find '%s'" % name + return None + +def InternalLaTeXAuxAction(XXXLaTeXAction, target = None, source= None, env=None): + """A builder for LaTeX files that checks the output in the aux file + and decides how many times to use LaTeXAction, and BibTeXAction.""" + + global must_rerun_latex + + # This routine is called with two actions. In this file for DVI builds + # with LaTeXAction and from the pdflatex.py with PDFLaTeXAction + # set this up now for the case where the user requests a different extension + # for the target filename + if (XXXLaTeXAction == LaTeXAction): + callerSuffix = ".dvi" + else: + callerSuffix = env['PDFSUFFIX'] + + basename = SCons.Util.splitext(str(source[0]))[0] + basedir = os.path.split(str(source[0]))[0] + basefile = os.path.split(str(basename))[1] + abspath = os.path.abspath(basedir) + + targetext = os.path.splitext(str(target[0]))[1] + targetdir = os.path.split(str(target[0]))[0] + + saved_env = {} + for var in SCons.Scanner.LaTeX.LaTeX.env_variables: + saved_env[var] = modify_env_var(env, var, abspath) + + # Create base file names with the target directory since the auxiliary files + # will be made there. That's because the *COM variables have the cd + # command in the prolog. We check + # for the existence of files before opening them--even ones like the + # aux file that TeX always creates--to make it possible to write tests + # with stubs that don't necessarily generate all of the same files. + + targetbase = os.path.join(targetdir, basefile) + + # if there is a \makeindex there will be a .idx and thus + # we have to run makeindex at least once to keep the build + # happy even if there is no index. + # Same for glossaries, nomenclature, and acronyms + src_content = source[0].get_text_contents() + run_makeindex = makeindex_re.search(src_content) and not os.path.isfile(targetbase + '.idx') + run_nomenclature = makenomenclature_re.search(src_content) and not os.path.isfile(targetbase + '.nlo') + run_glossary = makeglossary_re.search(src_content) and not os.path.isfile(targetbase + '.glo') + run_glossaries = makeglossaries_re.search(src_content) and not os.path.isfile(targetbase + '.glo') + run_acronyms = makeacronyms_re.search(src_content) and not os.path.isfile(targetbase + '.acn') + + saved_hashes = {} + suffix_nodes = {} + + + for suffix in all_suffixes+sum(newglossary_suffix, []): + theNode = env.fs.File(targetbase + suffix) + suffix_nodes[suffix] = theNode + saved_hashes[suffix] = theNode.get_csig() + + if Verbose: + print "hashes: ",saved_hashes + + must_rerun_latex = True + + # .aux files already processed by BibTex + already_bibtexed = [] + + # + # routine to update MD5 hash and compare + # + def check_MD5(filenode, suffix): + global must_rerun_latex + # two calls to clear old csig + filenode.clear_memoized_values() + filenode.ninfo = filenode.new_ninfo() + new_md5 = filenode.get_csig() + + if saved_hashes[suffix] == new_md5: + if Verbose: + print "file %s not changed" % (targetbase+suffix) + return False # unchanged + saved_hashes[suffix] = new_md5 + must_rerun_latex = True + if Verbose: + print "file %s changed, rerunning Latex, new hash = " % (targetbase+suffix), new_md5 + return True # changed + + # generate the file name that latex will generate + resultfilename = targetbase + callerSuffix + + count = 0 + + while (must_rerun_latex and count < int(env.subst('$LATEXRETRIES'))) : + result = XXXLaTeXAction(target, source, env) + if result != 0: + return result + + count = count + 1 + + must_rerun_latex = False + # Decide if various things need to be run, or run again. + + # Read the log file to find warnings/errors + logfilename = targetbase + '.log' + logContent = '' + if os.path.isfile(logfilename): + logContent = open(logfilename, "rb").read() + + + # Read the fls file to find all .aux files + flsfilename = targetbase + '.fls' + flsContent = '' + auxfiles = [] + if os.path.isfile(flsfilename): + flsContent = open(flsfilename, "rb").read() + auxfiles = openout_aux_re.findall(flsContent) + # remove duplicates + dups = {} + for x in auxfiles: + dups[x] = 1 + auxfiles = list(dups.keys()) + + bcffiles = [] + if os.path.isfile(flsfilename): + flsContent = open(flsfilename, "rb").read() + bcffiles = openout_bcf_re.findall(flsContent) + # remove duplicates + dups = {} + for x in bcffiles: + dups[x] = 1 + bcffiles = list(dups.keys()) + + if Verbose: + print "auxfiles ",auxfiles + print "bcffiles ",bcffiles + + # Now decide if bibtex will need to be run. + # The information that bibtex reads from the .aux file is + # pass-independent. If we find (below) that the .bbl file is unchanged, + # then the last latex saw a correct bibliography. + # Therefore only do this once + # Go through all .aux files and remember the files already done. + for auxfilename in auxfiles: + if auxfilename not in already_bibtexed: + already_bibtexed.append(auxfilename) + target_aux = os.path.join(targetdir, auxfilename) + if os.path.isfile(target_aux): + content = open(target_aux, "rb").read() + if content.find("bibdata") != -1: + if Verbose: + print "Need to run bibtex on ",auxfilename + bibfile = env.fs.File(SCons.Util.splitext(target_aux)[0]) + result = BibTeXAction(bibfile, bibfile, env) + if result != 0: + check_file_error_message(env['BIBTEX'], 'blg') + must_rerun_latex = True + + # Now decide if biber will need to be run. + # When the backend for biblatex is biber (by choice or default) the + # citation information is put in the .bcf file. + # The information that biber reads from the .bcf file is + # pass-independent. If we find (below) that the .bbl file is unchanged, + # then the last latex saw a correct bibliography. + # Therefore only do this once + # Go through all .bcf files and remember the files already done. + for bcffilename in bcffiles: + if bcffilename not in already_bibtexed: + already_bibtexed.append(bcffilename) + target_bcf = os.path.join(targetdir, bcffilename) + if os.path.isfile(target_bcf): + content = open(target_bcf, "rb").read() + if content.find("bibdata") != -1: + if Verbose: + print "Need to run biber on ",bcffilename + bibfile = env.fs.File(SCons.Util.splitext(target_bcf)[0]) + result = BiberAction(bibfile, bibfile, env) + if result != 0: + check_file_error_message(env['BIBER'], 'blg') + must_rerun_latex = True + + # Now decide if latex will need to be run again due to index. + if check_MD5(suffix_nodes['.idx'],'.idx') or (count == 1 and run_makeindex): + # We must run makeindex + if Verbose: + print "Need to run makeindex" + idxfile = suffix_nodes['.idx'] + result = MakeIndexAction(idxfile, idxfile, env) + if result != 0: + check_file_error_message(env['MAKEINDEX'], 'ilg') + return result + + # TO-DO: need to add a way for the user to extend this list for whatever + # auxiliary files they create in other (or their own) packages + # Harder is case is where an action needs to be called -- that should be rare (I hope?) + + for index in check_suffixes: + check_MD5(suffix_nodes[index],index) + + # Now decide if latex will need to be run again due to nomenclature. + if check_MD5(suffix_nodes['.nlo'],'.nlo') or (count == 1 and run_nomenclature): + # We must run makeindex + if Verbose: + print "Need to run makeindex for nomenclature" + nclfile = suffix_nodes['.nlo'] + result = MakeNclAction(nclfile, nclfile, env) + if result != 0: + check_file_error_message('%s (nomenclature)' % env['MAKENCL'], + 'nlg') + #return result + + # Now decide if latex will need to be run again due to glossary. + if check_MD5(suffix_nodes['.glo'],'.glo') or (count == 1 and run_glossaries) or (count == 1 and run_glossary): + # We must run makeindex + if Verbose: + print "Need to run makeindex for glossary" + glofile = suffix_nodes['.glo'] + result = MakeGlossaryAction(glofile, glofile, env) + if result != 0: + check_file_error_message('%s (glossary)' % env['MAKEGLOSSARY'], + 'glg') + #return result + + # Now decide if latex will need to be run again due to acronyms. + if check_MD5(suffix_nodes['.acn'],'.acn') or (count == 1 and run_acronyms): + # We must run makeindex + if Verbose: + print "Need to run makeindex for acronyms" + acrfile = suffix_nodes['.acn'] + result = MakeAcronymsAction(acrfile, acrfile, env) + if result != 0: + check_file_error_message('%s (acronyms)' % env['MAKEACRONYMS'], + 'alg') + return result + + # Now decide if latex will need to be run again due to newglossary command. + for ig in range(len(newglossary_suffix)): + if check_MD5(suffix_nodes[newglossary_suffix[ig][2]],newglossary_suffix[ig][2]) or (count == 1): + # We must run makeindex + if Verbose: + print "Need to run makeindex for newglossary" + newglfile = suffix_nodes[newglossary_suffix[ig][2]] + MakeNewGlossaryAction = SCons.Action.Action("$MAKENEWGLOSSARY ${SOURCE.filebase}%s -s ${SOURCE.filebase}.ist -t ${SOURCE.filebase}%s -o ${SOURCE.filebase}%s" % (newglossary_suffix[ig][2],newglossary_suffix[ig][0],newglossary_suffix[ig][1]), "$MAKENEWGLOSSARYCOMSTR") + + result = MakeNewGlossaryAction(newglfile, newglfile, env) + if result != 0: + check_file_error_message('%s (newglossary)' % env['MAKENEWGLOSSARY'], + newglossary_suffix[ig][0]) + return result + + # Now decide if latex needs to be run yet again to resolve warnings. + if warning_rerun_re.search(logContent): + must_rerun_latex = True + if Verbose: + print "rerun Latex due to latex or package rerun warning" + + if rerun_citations_re.search(logContent): + must_rerun_latex = True + if Verbose: + print "rerun Latex due to 'Rerun to get citations correct' warning" + + if undefined_references_re.search(logContent): + must_rerun_latex = True + if Verbose: + print "rerun Latex due to undefined references or citations" + + if (count >= int(env.subst('$LATEXRETRIES')) and must_rerun_latex): + print "reached max number of retries on Latex ,",int(env.subst('$LATEXRETRIES')) +# end of while loop + + # rename Latex's output to what the target name is + if not (str(target[0]) == resultfilename and os.path.isfile(resultfilename)): + if os.path.isfile(resultfilename): + print "move %s to %s" % (resultfilename, str(target[0]), ) + shutil.move(resultfilename,str(target[0])) + + # Original comment (when TEXPICTS was not restored): + # The TEXPICTS enviroment variable is needed by a dvi -> pdf step + # later on Mac OSX so leave it + # + # It is also used when searching for pictures (implicit dependencies). + # Why not set the variable again in the respective builder instead + # of leaving local modifications in the environment? What if multiple + # latex builds in different directories need different TEXPICTS? + for var in SCons.Scanner.LaTeX.LaTeX.env_variables: + if var == 'TEXPICTS': + continue + if saved_env[var] is _null: + try: + del env['ENV'][var] + except KeyError: + pass # was never set + else: + env['ENV'][var] = saved_env[var] + + return result + +def LaTeXAuxAction(target = None, source= None, env=None): + result = InternalLaTeXAuxAction( LaTeXAction, target, source, env ) + return result + +LaTeX_re = re.compile("\\\\document(style|class)") + +def is_LaTeX(flist,env,abspath): + """Scan a file list to decide if it's TeX- or LaTeX-flavored.""" + + # We need to scan files that are included in case the + # \documentclass command is in them. + + # get path list from both env['TEXINPUTS'] and env['ENV']['TEXINPUTS'] + savedpath = modify_env_var(env, 'TEXINPUTS', abspath) + paths = env['ENV']['TEXINPUTS'] + if SCons.Util.is_List(paths): + pass + else: + # Split at os.pathsep to convert into absolute path + paths = paths.split(os.pathsep) + + # now that we have the path list restore the env + if savedpath is _null: + try: + del env['ENV']['TEXINPUTS'] + except KeyError: + pass # was never set + else: + env['ENV']['TEXINPUTS'] = savedpath + if Verbose: + print "is_LaTeX search path ",paths + print "files to search :",flist + + # Now that we have the search path and file list, check each one + for f in flist: + if Verbose: + print " checking for Latex source ",str(f) + + content = f.get_text_contents() + if LaTeX_re.search(content): + if Verbose: + print "file %s is a LaTeX file" % str(f) + return 1 + if Verbose: + print "file %s is not a LaTeX file" % str(f) + + # now find included files + inc_files = [ ] + inc_files.extend( include_re.findall(content) ) + if Verbose: + print "files included by '%s': "%str(f),inc_files + # inc_files is list of file names as given. need to find them + # using TEXINPUTS paths. + + # search the included files + for src in inc_files: + srcNode = FindFile(src,['.tex','.ltx','.latex'],paths,env,requireExt=False) + # make this a list since is_LaTeX takes a list. + fileList = [srcNode,] + if Verbose: + print "FindFile found ",srcNode + if srcNode is not None: + file_test = is_LaTeX(fileList, env, abspath) + + # return on first file that finds latex is needed. + if file_test: + return file_test + + if Verbose: + print " done scanning ",str(f) + + return 0 + +def TeXLaTeXFunction(target = None, source= None, env=None): + """A builder for TeX and LaTeX that scans the source file to + decide the "flavor" of the source and then executes the appropriate + program.""" + + # find these paths for use in is_LaTeX to search for included files + basedir = os.path.split(str(source[0]))[0] + abspath = os.path.abspath(basedir) + + if is_LaTeX(source,env,abspath): + result = LaTeXAuxAction(target,source,env) + if result != 0: + check_file_error_message(env['LATEX']) + else: + result = TeXAction(target,source,env) + if result != 0: + check_file_error_message(env['TEX']) + return result + +def TeXLaTeXStrFunction(target = None, source= None, env=None): + """A strfunction for TeX and LaTeX that scans the source file to + decide the "flavor" of the source and then returns the appropriate + command string.""" + if env.GetOption("no_exec"): + + # find these paths for use in is_LaTeX to search for included files + basedir = os.path.split(str(source[0]))[0] + abspath = os.path.abspath(basedir) + + if is_LaTeX(source,env,abspath): + result = env.subst('$LATEXCOM',0,target,source)+" ..." + else: + result = env.subst("$TEXCOM",0,target,source)+" ..." + else: + result = '' + return result + +def tex_eps_emitter(target, source, env): + """An emitter for TeX and LaTeX sources when + executing tex or latex. It will accept .ps and .eps + graphics files + """ + (target, source) = tex_emitter_core(target, source, env, TexGraphics) + + return (target, source) + +def tex_pdf_emitter(target, source, env): + """An emitter for TeX and LaTeX sources when + executing pdftex or pdflatex. It will accept graphics + files of types .pdf, .jpg, .png, .gif, and .tif + """ + (target, source) = tex_emitter_core(target, source, env, LatexGraphics) + + return (target, source) + +def ScanFiles(theFile, target, paths, file_tests, file_tests_search, env, graphics_extensions, targetdir, aux_files): + """ For theFile (a Node) update any file_tests and search for graphics files + then find all included files and call ScanFiles recursively for each of them""" + + content = theFile.get_text_contents() + if Verbose: + print " scanning ",str(theFile) + + for i in range(len(file_tests_search)): + if file_tests[i][0] is None: + if Verbose: + print "scan i ",i," files_tests[i] ",file_tests[i], file_tests[i][1] + file_tests[i][0] = file_tests_search[i].search(content) + if Verbose and file_tests[i][0]: + print " found match for ",file_tests[i][1][-1] + # for newglossary insert the suffixes in file_tests[i] + if file_tests[i][0] and file_tests[i][1][-1] == 'newglossary': + findresult = file_tests_search[i].findall(content) + for l in range(len(findresult)) : + (file_tests[i][1]).insert(0,'.'+findresult[l][3]) + (file_tests[i][1]).insert(0,'.'+findresult[l][2]) + (file_tests[i][1]).insert(0,'.'+findresult[l][0]) + suffix_list = ['.'+findresult[l][0],'.'+findresult[l][2],'.'+findresult[l][3] ] + newglossary_suffix.append(suffix_list) + if Verbose: + print " new suffixes for newglossary ",newglossary_suffix + + + incResult = includeOnly_re.search(content) + if incResult: + aux_files.append(os.path.join(targetdir, incResult.group(1))) + if Verbose: + print "\include file names : ", aux_files + # recursively call this on each of the included files + inc_files = [ ] + inc_files.extend( include_re.findall(content) ) + if Verbose: + print "files included by '%s': "%str(theFile),inc_files + # inc_files is list of file names as given. need to find them + # using TEXINPUTS paths. + + for src in inc_files: + srcNode = FindFile(src,['.tex','.ltx','.latex'],paths,env,requireExt=False) + if srcNode is not None: + file_tests = ScanFiles(srcNode, target, paths, file_tests, file_tests_search, env, graphics_extensions, targetdir, aux_files) + if Verbose: + print " done scanning ",str(theFile) + return file_tests + +def tex_emitter_core(target, source, env, graphics_extensions): + """An emitter for TeX and LaTeX sources. + For LaTeX sources we try and find the common created files that + are needed on subsequent runs of latex to finish tables of contents, + bibliographies, indices, lists of figures, and hyperlink references. + """ + basename = SCons.Util.splitext(str(source[0]))[0] + basefile = os.path.split(str(basename))[1] + targetdir = os.path.split(str(target[0]))[0] + targetbase = os.path.join(targetdir, basefile) + + basedir = os.path.split(str(source[0]))[0] + abspath = os.path.abspath(basedir) + target[0].attributes.path = abspath + + # + # file names we will make use of in searching the sources and log file + # + emit_suffixes = ['.aux', '.log', '.ilg', '.blg', '.nls', '.nlg', '.gls', '.glg', '.alg'] + all_suffixes + auxfilename = targetbase + '.aux' + logfilename = targetbase + '.log' + flsfilename = targetbase + '.fls' + + env.SideEffect(auxfilename,target[0]) + env.SideEffect(logfilename,target[0]) + env.SideEffect(flsfilename,target[0]) + if Verbose: + print "side effect :",auxfilename,logfilename,flsfilename + env.Clean(target[0],auxfilename) + env.Clean(target[0],logfilename) + env.Clean(target[0],flsfilename) + + content = source[0].get_text_contents() + + # These variables are no longer used. + #idx_exists = os.path.isfile(targetbase + '.idx') + #nlo_exists = os.path.isfile(targetbase + '.nlo') + #glo_exists = os.path.isfile(targetbase + '.glo') + #acr_exists = os.path.isfile(targetbase + '.acn') + + # set up list with the regular expressions + # we use to find features used + file_tests_search = [auxfile_re, + makeindex_re, + bibliography_re, + bibunit_re, + multibib_re, + addbibresource_re, + tableofcontents_re, + listoffigures_re, + listoftables_re, + hyperref_re, + makenomenclature_re, + makeglossary_re, + makeglossaries_re, + makeacronyms_re, + beamer_re, + newglossary_re ] + # set up list with the file suffixes that need emitting + # when a feature is found + file_tests_suff = [['.aux','aux_file'], + ['.idx', '.ind', '.ilg','makeindex'], + ['.bbl', '.blg','bibliography'], + ['.bbl', '.blg','bibunit'], + ['.bbl', '.blg','multibib'], + ['.bbl', '.blg','.bcf','addbibresource'], + ['.toc','contents'], + ['.lof','figures'], + ['.lot','tables'], + ['.out','hyperref'], + ['.nlo', '.nls', '.nlg','nomenclature'], + ['.glo', '.gls', '.glg','glossary'], + ['.glo', '.gls', '.glg','glossaries'], + ['.acn', '.acr', '.alg','acronyms'], + ['.nav', '.snm', '.out', '.toc','beamer'], + ['newglossary',] ] + # for newglossary the suffixes are added as we find the command + # build the list of lists + file_tests = [] + for i in range(len(file_tests_search)): + file_tests.append( [None, file_tests_suff[i]] ) + + # TO-DO: need to add a way for the user to extend this list for whatever + # auxiliary files they create in other (or their own) packages + + # get path list from both env['TEXINPUTS'] and env['ENV']['TEXINPUTS'] + savedpath = modify_env_var(env, 'TEXINPUTS', abspath) + paths = env['ENV']['TEXINPUTS'] + if SCons.Util.is_List(paths): + pass + else: + # Split at os.pathsep to convert into absolute path + paths = paths.split(os.pathsep) + + # now that we have the path list restore the env + if savedpath is _null: + try: + del env['ENV']['TEXINPUTS'] + except KeyError: + pass # was never set + else: + env['ENV']['TEXINPUTS'] = savedpath + if Verbose: + print "search path ",paths + + # scan all sources for side effect files + aux_files = [] + file_tests = ScanFiles(source[0], target, paths, file_tests, file_tests_search, env, graphics_extensions, targetdir, aux_files) + + for (theSearch,suffix_list) in file_tests: + # add side effects if feature is present.If file is to be generated,add all side effects + if Verbose and theSearch: + print "check side effects for ",suffix_list[-1] + if (theSearch != None) or (not source[0].exists() ): + file_list = [targetbase,] + # for bibunit we need a list of files + if suffix_list[-1] == 'bibunit': + file_basename = os.path.join(targetdir, 'bu*.aux') + file_list = glob.glob(file_basename) + # remove the suffix '.aux' + for i in range(len(file_list)): + file_list.append(SCons.Util.splitext(file_list[i])[0]) + # for multibib we need a list of files + if suffix_list[-1] == 'multibib': + for multibibmatch in multibib_re.finditer(content): + if Verbose: + print "multibib match ",multibibmatch.group(1) + if multibibmatch != None: + baselist = multibibmatch.group(1).split(',') + if Verbose: + print "multibib list ", baselist + for i in range(len(baselist)): + file_list.append(os.path.join(targetdir, baselist[i])) + # now define the side effects + for file_name in file_list: + for suffix in suffix_list[:-1]: + env.SideEffect(file_name + suffix,target[0]) + if Verbose: + print "side effect tst :",file_name + suffix, " target is ",str(target[0]) + env.Clean(target[0],file_name + suffix) + + for aFile in aux_files: + aFile_base = SCons.Util.splitext(aFile)[0] + env.SideEffect(aFile_base + '.aux',target[0]) + if Verbose: + print "side effect aux :",aFile_base + '.aux' + env.Clean(target[0],aFile_base + '.aux') + # read fls file to get all other files that latex creates and will read on the next pass + # remove files from list that we explicitly dealt with above + if os.path.isfile(flsfilename): + content = open(flsfilename, "rb").read() + out_files = openout_re.findall(content) + myfiles = [auxfilename, logfilename, flsfilename, targetbase+'.dvi',targetbase+'.pdf'] + for filename in out_files[:]: + if filename in myfiles: + out_files.remove(filename) + env.SideEffect(out_files,target[0]) + if Verbose: + print "side effect fls :",out_files + env.Clean(target[0],out_files) + + return (target, source) + + +TeXLaTeXAction = None + +def generate(env): + """Add Builders and construction variables for TeX to an Environment.""" + + global TeXLaTeXAction + if TeXLaTeXAction is None: + TeXLaTeXAction = SCons.Action.Action(TeXLaTeXFunction, + strfunction=TeXLaTeXStrFunction) + + env.AppendUnique(LATEXSUFFIXES=SCons.Tool.LaTeXSuffixes) + + generate_common(env) + + import dvi + dvi.generate(env) + + bld = env['BUILDERS']['DVI'] + bld.add_action('.tex', TeXLaTeXAction) + bld.add_emitter('.tex', tex_eps_emitter) + +def generate_darwin(env): + try: + environ = env['ENV'] + except KeyError: + environ = {} + env['ENV'] = environ + + if (platform.system() == 'Darwin'): + try: + ospath = env['ENV']['PATHOSX'] + except: + ospath = None + if ospath: + env.AppendENVPath('PATH', ospath) + +def generate_common(env): + """Add internal Builders and construction variables for LaTeX to an Environment.""" + + # Add OSX system paths so TeX tools can be found + # when a list of tools is given the exists() method is not called + generate_darwin(env) + + # A generic tex file Action, sufficient for all tex files. + global TeXAction + if TeXAction is None: + TeXAction = SCons.Action.Action("$TEXCOM", "$TEXCOMSTR") + + # An Action to build a latex file. This might be needed more + # than once if we are dealing with labels and bibtex. + global LaTeXAction + if LaTeXAction is None: + LaTeXAction = SCons.Action.Action("$LATEXCOM", "$LATEXCOMSTR") + + # Define an action to run BibTeX on a file. + global BibTeXAction + if BibTeXAction is None: + BibTeXAction = SCons.Action.Action("$BIBTEXCOM", "$BIBTEXCOMSTR") + + # Define an action to run Biber on a file. + global BiberAction + if BiberAction is None: + BiberAction = SCons.Action.Action("$BIBERCOM", "$BIBERCOMSTR") + + # Define an action to run MakeIndex on a file. + global MakeIndexAction + if MakeIndexAction is None: + MakeIndexAction = SCons.Action.Action("$MAKEINDEXCOM", "$MAKEINDEXCOMSTR") + + # Define an action to run MakeIndex on a file for nomenclatures. + global MakeNclAction + if MakeNclAction is None: + MakeNclAction = SCons.Action.Action("$MAKENCLCOM", "$MAKENCLCOMSTR") + + # Define an action to run MakeIndex on a file for glossaries. + global MakeGlossaryAction + if MakeGlossaryAction is None: + MakeGlossaryAction = SCons.Action.Action("$MAKEGLOSSARYCOM", "$MAKEGLOSSARYCOMSTR") + + # Define an action to run MakeIndex on a file for acronyms. + global MakeAcronymsAction + if MakeAcronymsAction is None: + MakeAcronymsAction = SCons.Action.Action("$MAKEACRONYMSCOM", "$MAKEACRONYMSCOMSTR") + + try: + environ = env['ENV'] + except KeyError: + environ = {} + env['ENV'] = environ + + # Some Linux platforms have pdflatex set up in a way + # that requires that the HOME environment variable be set. + # Add it here if defined. + v = os.environ.get('HOME') + if v: + environ['HOME'] = v + + CDCOM = 'cd ' + if platform.system() == 'Windows': + # allow cd command to change drives on Windows + CDCOM = 'cd /D ' + + env['TEX'] = 'tex' + env['TEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode -recorder') + env['TEXCOM'] = CDCOM + '${TARGET.dir} && $TEX $TEXFLAGS ${SOURCE.file}' + + env['PDFTEX'] = 'pdftex' + env['PDFTEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode -recorder') + env['PDFTEXCOM'] = CDCOM + '${TARGET.dir} && $PDFTEX $PDFTEXFLAGS ${SOURCE.file}' + + env['LATEX'] = 'latex' + env['LATEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode -recorder') + env['LATEXCOM'] = CDCOM + '${TARGET.dir} && $LATEX $LATEXFLAGS ${SOURCE.file}' + env['LATEXRETRIES'] = 4 + + env['PDFLATEX'] = 'pdflatex' + env['PDFLATEXFLAGS'] = SCons.Util.CLVar('-interaction=nonstopmode -recorder') + env['PDFLATEXCOM'] = CDCOM + '${TARGET.dir} && $PDFLATEX $PDFLATEXFLAGS ${SOURCE.file}' + + env['BIBTEX'] = 'bibtex' + env['BIBTEXFLAGS'] = SCons.Util.CLVar('') + env['BIBTEXCOM'] = CDCOM + '${TARGET.dir} && $BIBTEX $BIBTEXFLAGS ${SOURCE.filebase}' + + env['BIBER'] = 'biber' + env['BIBERFLAGS'] = SCons.Util.CLVar('') + env['BIBERCOM'] = CDCOM + '${TARGET.dir} && $BIBER $BIBERFLAGS ${SOURCE.filebase}' + + env['MAKEINDEX'] = 'makeindex' + env['MAKEINDEXFLAGS'] = SCons.Util.CLVar('') + env['MAKEINDEXCOM'] = CDCOM + '${TARGET.dir} && $MAKEINDEX $MAKEINDEXFLAGS ${SOURCE.file}' + + env['MAKEGLOSSARY'] = 'makeindex' + env['MAKEGLOSSARYSTYLE'] = '${SOURCE.filebase}.ist' + env['MAKEGLOSSARYFLAGS'] = SCons.Util.CLVar('-s ${MAKEGLOSSARYSTYLE} -t ${SOURCE.filebase}.glg') + env['MAKEGLOSSARYCOM'] = CDCOM + '${TARGET.dir} && $MAKEGLOSSARY ${SOURCE.filebase}.glo $MAKEGLOSSARYFLAGS -o ${SOURCE.filebase}.gls' + + env['MAKEACRONYMS'] = 'makeindex' + env['MAKEACRONYMSSTYLE'] = '${SOURCE.filebase}.ist' + env['MAKEACRONYMSFLAGS'] = SCons.Util.CLVar('-s ${MAKEACRONYMSSTYLE} -t ${SOURCE.filebase}.alg') + env['MAKEACRONYMSCOM'] = CDCOM + '${TARGET.dir} && $MAKEACRONYMS ${SOURCE.filebase}.acn $MAKEACRONYMSFLAGS -o ${SOURCE.filebase}.acr' + + env['MAKENCL'] = 'makeindex' + env['MAKENCLSTYLE'] = 'nomencl.ist' + env['MAKENCLFLAGS'] = '-s ${MAKENCLSTYLE} -t ${SOURCE.filebase}.nlg' + env['MAKENCLCOM'] = CDCOM + '${TARGET.dir} && $MAKENCL ${SOURCE.filebase}.nlo $MAKENCLFLAGS -o ${SOURCE.filebase}.nls' + + env['MAKENEWGLOSSARY'] = 'makeindex' + env['MAKENEWGLOSSARYCOM'] = CDCOM + '${TARGET.dir} && $MAKENEWGLOSSARY ' + +def exists(env): + generate_darwin(env) + return env.Detect('tex') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/textfile.py b/scons/scons-local-2.3.0/SCons/Tool/textfile.py new file mode 100644 index 000000000..5e8307bf0 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/textfile.py @@ -0,0 +1,175 @@ +# -*- python -*- +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__doc__ = """ +Textfile/Substfile builder for SCons. + + Create file 'target' which typically is a textfile. The 'source' + may be any combination of strings, Nodes, or lists of same. A + 'linesep' will be put between any part written and defaults to + os.linesep. + + The only difference between the Textfile builder and the Substfile + builder is that strings are converted to Value() nodes for the + former and File() nodes for the latter. To insert files in the + former or strings in the latter, wrap them in a File() or Value(), + respectively. + + The values of SUBST_DICT first have any construction variables + expanded (its keys are not expanded). If a value of SUBST_DICT is + a python callable function, it is called and the result is expanded + as the value. Values are substituted in a "random" order; if any + substitution could be further expanded by another subsitition, it + is unpredictible whether the expansion will occur. +""" + +__revision__ = "src/engine/SCons/Tool/textfile.py 2013/03/03 09:48:35 garyo" + +import SCons + +import os +import re + +from SCons.Node import Node +from SCons.Node.Python import Value +from SCons.Util import is_String, is_Sequence, is_Dict + +def _do_subst(node, subs): + """ + Fetch the node contents and replace all instances of the keys with + their values. For example, if subs is + {'%VERSION%': '1.2345', '%BASE%': 'MyProg', '%prefix%': '/bin'}, + then all instances of %VERSION% in the file will be replaced with + 1.2345 and so forth. + """ + contents = node.get_text_contents() + if not subs: return contents + for (k,v) in subs: + contents = re.sub(k, v, contents) + return contents + +def _action(target, source, env): + # prepare the line separator + linesep = env['LINESEPARATOR'] + if linesep is None: + linesep = os.linesep + elif is_String(linesep): + pass + elif isinstance(linesep, Value): + linesep = linesep.get_text_contents() + else: + raise SCons.Errors.UserError( + 'unexpected type/class for LINESEPARATOR: %s' + % repr(linesep), None) + + # create a dictionary to use for the substitutions + if 'SUBST_DICT' not in env: + subs = None # no substitutions + else: + d = env['SUBST_DICT'] + if is_Dict(d): + d = list(d.items()) + elif is_Sequence(d): + pass + else: + raise SCons.Errors.UserError('SUBST_DICT must be dict or sequence') + subs = [] + for (k,v) in d: + if callable(v): + v = v() + if is_String(v): + v = env.subst(v) + else: + v = str(v) + subs.append((k,v)) + + # write the file + try: + fd = open(target[0].get_path(), "wb") + except (OSError,IOError), e: + raise SCons.Errors.UserError("Can't write target file %s" % target[0]) + # separate lines by 'linesep' only if linesep is not empty + lsep = None + for s in source: + if lsep: fd.write(lsep) + fd.write(_do_subst(s, subs)) + lsep = linesep + fd.close() + +def _strfunc(target, source, env): + return "Creating '%s'" % target[0] + +def _convert_list_R(newlist, sources): + for elem in sources: + if is_Sequence(elem): + _convert_list_R(newlist, elem) + elif isinstance(elem, Node): + newlist.append(elem) + else: + newlist.append(Value(elem)) +def _convert_list(target, source, env): + if len(target) != 1: + raise SCons.Errors.UserError("Only one target file allowed") + newlist = [] + _convert_list_R(newlist, source) + return target, newlist + +_common_varlist = ['SUBST_DICT', 'LINESEPARATOR'] + +_text_varlist = _common_varlist + ['TEXTFILEPREFIX', 'TEXTFILESUFFIX'] +_text_builder = SCons.Builder.Builder( + action = SCons.Action.Action(_action, _strfunc, varlist = _text_varlist), + source_factory = Value, + emitter = _convert_list, + prefix = '$TEXTFILEPREFIX', + suffix = '$TEXTFILESUFFIX', + ) + +_subst_varlist = _common_varlist + ['SUBSTFILEPREFIX', 'TEXTFILESUFFIX'] +_subst_builder = SCons.Builder.Builder( + action = SCons.Action.Action(_action, _strfunc, varlist = _subst_varlist), + source_factory = SCons.Node.FS.File, + emitter = _convert_list, + prefix = '$SUBSTFILEPREFIX', + suffix = '$SUBSTFILESUFFIX', + src_suffix = ['.in'], + ) + +def generate(env): + env['LINESEPARATOR'] = os.linesep + env['BUILDERS']['Textfile'] = _text_builder + env['TEXTFILEPREFIX'] = '' + env['TEXTFILESUFFIX'] = '.txt' + env['BUILDERS']['Substfile'] = _subst_builder + env['SUBSTFILEPREFIX'] = '' + env['SUBSTFILESUFFIX'] = '' + +def exists(env): + return 1 + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/tlib.py b/scons/scons-local-2.3.0/SCons/Tool/tlib.py new file mode 100644 index 000000000..5a30712ff --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/tlib.py @@ -0,0 +1,53 @@ +"""SCons.Tool.tlib + +XXX + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/tlib.py 2013/03/03 09:48:35 garyo" + +import SCons.Tool +import SCons.Tool.bcc32 +import SCons.Util + +def generate(env): + SCons.Tool.bcc32.findIt('tlib', env) + """Add Builders and construction variables for ar to an Environment.""" + SCons.Tool.createStaticLibBuilder(env) + env['AR'] = 'tlib' + env['ARFLAGS'] = SCons.Util.CLVar('') + env['ARCOM'] = '$AR $TARGET $ARFLAGS /a $SOURCES' + env['LIBPREFIX'] = '' + env['LIBSUFFIX'] = '.lib' + +def exists(env): + return SCons.Tool.bcc32.findIt('tlib', env) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/wix.py b/scons/scons-local-2.3.0/SCons/Tool/wix.py new file mode 100644 index 000000000..5b4c606bf --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/wix.py @@ -0,0 +1,104 @@ +"""SCons.Tool.wix + +Tool-specific initialization for wix, the Windows Installer XML Tool. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/wix.py 2013/03/03 09:48:35 garyo" + +import SCons.Builder +import SCons.Action +import os + +def generate(env): + """Add Builders and construction variables for WiX to an Environment.""" + if not exists(env): + return + + env['WIXCANDLEFLAGS'] = ['-nologo'] + env['WIXCANDLEINCLUDE'] = [] + env['WIXCANDLECOM'] = '$WIXCANDLE $WIXCANDLEFLAGS -I $WIXCANDLEINCLUDE -o ${TARGET} ${SOURCE}' + + env['WIXLIGHTFLAGS'].append( '-nologo' ) + env['WIXLIGHTCOM'] = "$WIXLIGHT $WIXLIGHTFLAGS -out ${TARGET} ${SOURCES}" + env['WIXSRCSUF'] = '.wxs' + env['WIXOBJSUF'] = '.wixobj' + + object_builder = SCons.Builder.Builder( + action = '$WIXCANDLECOM', + suffix = '$WIXOBJSUF', + src_suffix = '$WIXSRCSUF') + + linker_builder = SCons.Builder.Builder( + action = '$WIXLIGHTCOM', + src_suffix = '$WIXOBJSUF', + src_builder = object_builder) + + env['BUILDERS']['WiX'] = linker_builder + +def exists(env): + env['WIXCANDLE'] = 'candle.exe' + env['WIXLIGHT'] = 'light.exe' + + # try to find the candle.exe and light.exe tools and + # add the install directory to light libpath. + for path in os.environ['PATH'].split(os.pathsep): + if not path: + continue + + # workaround for some weird python win32 bug. + if path[0] == '"' and path[-1:]=='"': + path = path[1:-1] + + # normalize the path + path = os.path.normpath(path) + + # search for the tools in the PATH environment variable + try: + files = os.listdir(path) + if env['WIXCANDLE'] in files and env['WIXLIGHT'] in files: + env.PrependENVPath('PATH', path) + # include appropriate flags if running WiX 2.0 + if 'wixui.wixlib' in files and 'WixUI_en-us.wxl' in files: + env['WIXLIGHTFLAGS'] = [ os.path.join( path, 'wixui.wixlib' ), + '-loc', + os.path.join( path, 'WixUI_en-us.wxl' ) ] + else: + env['WIXLIGHTFLAGS'] = [] + return 1 + except OSError: + pass # ignore this, could be a stale PATH entry. + + return None + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/xgettext.py b/scons/scons-local-2.3.0/SCons/Tool/xgettext.py new file mode 100644 index 000000000..6de021ace --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/xgettext.py @@ -0,0 +1,339 @@ +""" xgettext tool + +Tool specific initialization of `xgettext` tool. +""" + +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Tool/xgettext.py 2013/03/03 09:48:35 garyo" + +############################################################################# +class _CmdRunner(object): + """ Callabe object, which runs shell command storing its stdout and stderr to + variables. It also provides `strfunction()` method, which shall be used by + scons Action objects to print command string. """ + + def __init__( self, command, commandstr = None): + self.out = None + self.err = None + self.status = None + self.command = command + self.commandstr = commandstr + + def __call__(self, target, source, env): + import SCons.Action + import subprocess + import os + import sys + kw = { + 'stdin' : 'devnull', + 'stdout' : subprocess.PIPE, + 'stderr' : subprocess.PIPE, + 'universal_newlines' : True, + 'shell' : True + } + command = env.subst(self.command, target = target, source = source) + proc = SCons.Action._subproc(env, command, **kw) + self.out, self.err = proc.communicate() + self.status = proc.wait() + if self.err: sys.stderr.write(unicode(self.err)) + return self.status + + def strfunction(self, target, source, env): + import os + comstr = self.commandstr + if env.subst(comstr, target = target, source = source) == "": + comstr = self.command + s = env.subst(comstr, target = target, source = source) + return s +############################################################################# + +############################################################################# +def _update_pot_file(target, source, env): + """ Action function for `POTUpdate` builder """ + import re + import os + import SCons.Action + nop = lambda target, source, env : 0 + + # Save scons cwd and os cwd (NOTE: they may be different. After the job, we + # revert ech one to its original state). + save_cwd = env.fs.getcwd() + save_os_cwd = os.getcwd() + chdir = target[0].dir + chdir_str = repr(chdir.get_abspath()) + # Print chdir message (employ SCons.Action.Action for that. It knows better + # than me how to to this correctly). + env.Execute(SCons.Action.Action(nop, "Entering " + chdir_str)) + # Go to target's directory and do our job + env.fs.chdir(chdir, 1) # Go into target's directory + try: + cmd = _CmdRunner('$XGETTEXTCOM', '$XGETTEXTCOMSTR') + action = SCons.Action.Action(cmd, strfunction=cmd.strfunction) + status = action([ target[0] ], source, env) + except: + # Something went wrong. + env.Execute(SCons.Action.Action(nop, "Leaving " + chdir_str)) + # Revert working dirs to previous state and re-throw exception. + env.fs.chdir(save_cwd, 0) + os.chdir(save_os_cwd) + raise + # Print chdir message. + env.Execute(SCons.Action.Action(nop, "Leaving " + chdir_str)) + # Revert working dirs to previous state. + env.fs.chdir(save_cwd, 0) + os.chdir(save_os_cwd) + # If the command was not successfull, return error code. + if status: return status + + new_content = cmd.out + + if not new_content: + # When xgettext finds no internationalized messages, no *.pot is created + # (because we don't want to bother translators with empty POT files). + needs_update = False + explain = "no internationalized messages encountered" + else: + if target[0].exists(): + # If the file already exists, it's left unaltered unless its messages + # are outdated (w.r.t. to these recovered by xgettext from sources). + old_content = target[0].get_text_contents() + re_cdate = re.compile(r'^"POT-Creation-Date: .*"$[\r\n]?', re.M) + old_content_nocdate = re.sub(re_cdate,"",old_content) + new_content_nocdate = re.sub(re_cdate,"",new_content) + if(old_content_nocdate == new_content_nocdate): + # Messages are up-to-date + needs_update = False + explain = "messages in file found to be up-to-date" + else: + # Messages are outdated + needs_update = True + explain = "messages in file were outdated" + else: + # No POT file found, create new one + needs_update = True + explain = "new file" + if needs_update: + # Print message employing SCons.Action.Action for that. + msg = "Writting " + repr(str(target[0])) + " (" + explain + ")" + env.Execute(SCons.Action.Action(nop, msg)) + f = open(str(target[0]),"w") + f.write(new_content) + f.close() + return 0 + else: + # Print message employing SCons.Action.Action for that. + msg = "Not writting " + repr(str(target[0])) + " (" + explain + ")" + env.Execute(SCons.Action.Action(nop, msg)) + return 0 +############################################################################# + +############################################################################# +from SCons.Builder import BuilderBase +############################################################################# +class _POTBuilder(BuilderBase): + def _execute(self, env, target, source, *args): + if not target: + if env.has_key('POTDOMAIN') and env['POTDOMAIN']: + domain = env['POTDOMAIN'] + else: + domain = 'messages' + target = [ domain ] + return BuilderBase._execute(self, env, target, source, *args) +############################################################################# + +############################################################################# +def _scan_xgettext_from_files(target, source, env, files = None, path = None): + """ Parses `POTFILES.in`-like file and returns list of extracted file names. + """ + import re + import SCons.Util + import SCons.Node.FS + + if files is None: + return 0 + if not SCons.Util.is_List(files): + files = [ files ] + + if path is None: + if env.has_key('XGETTEXTPATH'): + path = env['XGETTEXTPATH'] + else: + path = [] + if not SCons.Util.is_List(path): + path = [ path ] + + path = SCons.Util.flatten(path) + + dirs = () + for p in path: + if not isinstance(p, SCons.Node.FS.Base): + if SCons.Util.is_String(p): + p = env.subst(p, source = source, target = target) + p = env.arg2nodes(p, env.fs.Dir) + dirs += tuple(p) + # cwd is the default search path (when no path is defined by user) + if not dirs: + dirs = (env.fs.getcwd(),) + + # Parse 'POTFILE.in' files. + re_comment = re.compile(r'^#[^\n\r]*$\r?\n?', re.M) + re_emptyln = re.compile(r'^[ \t\r]*$\r?\n?', re.M) + re_trailws = re.compile(r'[ \t\r]+$') + for f in files: + # Find files in search path $XGETTEXTPATH + if isinstance(f, SCons.Node.FS.Base) and f.rexists(): + contents = f.get_text_contents() + contents = re_comment.sub("", contents) + contents = re_emptyln.sub("", contents) + contents = re_trailws.sub("", contents) + depnames = contents.splitlines() + for depname in depnames: + depfile = SCons.Node.FS.find_file(depname, dirs) + if not depfile: + depfile = env.arg2nodes(depname, dirs[0].File) + env.Depends(target, depfile) + return 0 +############################################################################# + +############################################################################# +def _pot_update_emitter(target, source, env): + """ Emitter function for `POTUpdate` builder """ + from SCons.Tool.GettextCommon import _POTargetFactory + import SCons.Util + import SCons.Node.FS + + if env.has_key('XGETTEXTFROM'): + xfrom = env['XGETTEXTFROM'] + else: + return target, source + if not SCons.Util.is_List(xfrom): + xfrom = [ xfrom ] + + xfrom = SCons.Util.flatten(xfrom) + + # Prepare list of 'POTFILE.in' files. + files = [] + for xf in xfrom: + if not isinstance(xf, SCons.Node.FS.Base): + if SCons.Util.is_String(xf): + # Interpolate variables in strings + xf = env.subst(xf, source = source, target = target) + xf = env.arg2nodes(xf) + files.extend(xf) + if files: + env.Depends(target, files) + _scan_xgettext_from_files(target, source, env, files) + return target, source +############################################################################# + +############################################################################# +from SCons.Environment import _null +############################################################################# +def _POTUpdateBuilderWrapper(env, target=None, source=_null, **kw): + return env._POTUpdateBuilder(target, source, **kw) +############################################################################# + +############################################################################# +def _POTUpdateBuilder(env, **kw): + """ Creates `POTUpdate` builder object """ + import SCons.Action + from SCons.Tool.GettextCommon import _POTargetFactory + kw['action'] = SCons.Action.Action(_update_pot_file, None) + kw['suffix'] = '$POTSUFFIX' + kw['target_factory'] = _POTargetFactory(env, alias='$POTUPDATE_ALIAS').File + kw['emitter'] = _pot_update_emitter + return _POTBuilder(**kw) +############################################################################# + +############################################################################# +def generate(env,**kw): + """ Generate `xgettext` tool """ + import SCons.Util + from SCons.Tool.GettextCommon import RPaths, _detect_xgettext + + try: + env['XGETTEXT'] = _detect_xgettext(env) + except: + env['XGETTEXT'] = 'xgettext' + # NOTE: sources="$SOURCES" would work as well. However, we use following + # construction to convert absolute paths provided by scons onto paths + # relative to current working dir. Note, that scons expands $SOURCE(S) to + # absolute paths for sources $SOURCE(s) outside of current subtree (e.g. in + # "../"). With source=$SOURCE these absolute paths would be written to the + # resultant *.pot file (and its derived *.po files) as references to lines in + # source code (e.g. referring lines in *.c files). Such references would be + # correct (e.g. in poedit) only on machine on which *.pot was generated and + # would be of no use on other hosts (having a copy of source code located + # in different place in filesystem). + sources = '$( ${_concat( "", SOURCES, "", __env__, XgettextRPaths, TARGET' \ + + ', SOURCES)} $)' + + # NOTE: the output from $XGETTEXTCOM command must go to stdout, not to a file. + # This is required by the POTUpdate builder's action. + xgettextcom = '$XGETTEXT $XGETTEXTFLAGS $_XGETTEXTPATHFLAGS' \ + + ' $_XGETTEXTFROMFLAGS -o - ' + sources + + xgettextpathflags = '$( ${_concat( XGETTEXTPATHPREFIX, XGETTEXTPATH' \ + + ', XGETTEXTPATHSUFFIX, __env__, RDirs, TARGET, SOURCES)} $)' + xgettextfromflags = '$( ${_concat( XGETTEXTFROMPREFIX, XGETTEXTFROM' \ + + ', XGETTEXTFROMSUFFIX, __env__, target=TARGET, source=SOURCES)} $)' + + env.SetDefault( + _XGETTEXTDOMAIN = '${TARGET.filebase}', + XGETTEXTFLAGS = [ ], + XGETTEXTCOM = xgettextcom, + XGETTEXTCOMSTR = '', + XGETTEXTPATH = [ ], + XGETTEXTPATHPREFIX = '-D', + XGETTEXTPATHSUFFIX = '', + XGETTEXTFROM = None, + XGETTEXTFROMPREFIX = '-f', + XGETTEXTFROMSUFFIX = '', + _XGETTEXTPATHFLAGS = xgettextpathflags, + _XGETTEXTFROMFLAGS = xgettextfromflags, + POTSUFFIX = ['.pot'], + POTUPDATE_ALIAS = 'pot-update', + XgettextRPaths = RPaths(env) + ) + env.Append( BUILDERS = { + '_POTUpdateBuilder' : _POTUpdateBuilder(env) + } ) + env.AddMethod(_POTUpdateBuilderWrapper, 'POTUpdate') + env.AlwaysBuild(env.Alias('$POTUPDATE_ALIAS')) +############################################################################# + +############################################################################# +def exists(env): + """ Check, whether the tool exists """ + from SCons.Tool.GettextCommon import _xgettext_exists + try: + return _xgettext_exists(env) + except: + return False +############################################################################# + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/yacc.py b/scons/scons-local-2.3.0/SCons/Tool/yacc.py new file mode 100644 index 000000000..1934181f7 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/yacc.py @@ -0,0 +1,140 @@ +"""SCons.Tool.yacc + +Tool-specific initialization for yacc. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/yacc.py 2013/03/03 09:48:35 garyo" + +import os.path + +import SCons.Defaults +import SCons.Tool +import SCons.Util + +YaccAction = SCons.Action.Action("$YACCCOM", "$YACCCOMSTR") + +def _yaccEmitter(target, source, env, ysuf, hsuf): + yaccflags = env.subst("$YACCFLAGS", target=target, source=source) + flags = SCons.Util.CLVar(yaccflags) + targetBase, targetExt = os.path.splitext(SCons.Util.to_String(target[0])) + + if '.ym' in ysuf: # If using Objective-C + target = [targetBase + ".m"] # the extension is ".m". + + + # If -d is specified on the command line, yacc will emit a .h + # or .hpp file with the same name as the .c or .cpp output file. + if '-d' in flags: + target.append(targetBase + env.subst(hsuf, target=target, source=source)) + + # If -g is specified on the command line, yacc will emit a .vcg + # file with the same base name as the .y, .yacc, .ym or .yy file. + if "-g" in flags: + base, ext = os.path.splitext(SCons.Util.to_String(source[0])) + target.append(base + env.subst("$YACCVCGFILESUFFIX")) + + # If -v is specirfied yacc will create the output debug file + # which is not really source for any process, but should + # be noted and also be cleaned + # Bug #2558 + if "-v" in flags: + env.SideEffect(targetBase+'.output',target[0]) + env.Clean(target[0],targetBase+'.output') + + + + # With --defines and --graph, the name of the file is totally defined + # in the options. + fileGenOptions = ["--defines=", "--graph="] + for option in flags: + for fileGenOption in fileGenOptions: + l = len(fileGenOption) + if option[:l] == fileGenOption: + # A file generating option is present, so add the file + # name to the list of targets. + fileName = option[l:].strip() + target.append(fileName) + + return (target, source) + +def yEmitter(target, source, env): + return _yaccEmitter(target, source, env, ['.y', '.yacc'], '$YACCHFILESUFFIX') + +def ymEmitter(target, source, env): + return _yaccEmitter(target, source, env, ['.ym'], '$YACCHFILESUFFIX') + +def yyEmitter(target, source, env): + return _yaccEmitter(target, source, env, ['.yy'], '$YACCHXXFILESUFFIX') + +def generate(env): + """Add Builders and construction variables for yacc to an Environment.""" + c_file, cxx_file = SCons.Tool.createCFileBuilders(env) + + # C + c_file.add_action('.y', YaccAction) + c_file.add_emitter('.y', yEmitter) + + c_file.add_action('.yacc', YaccAction) + c_file.add_emitter('.yacc', yEmitter) + + # Objective-C + c_file.add_action('.ym', YaccAction) + c_file.add_emitter('.ym', ymEmitter) + + # C++ + cxx_file.add_action('.yy', YaccAction) + cxx_file.add_emitter('.yy', yyEmitter) + + env['YACC'] = env.Detect('bison') or 'yacc' + env['YACCFLAGS'] = SCons.Util.CLVar('') + env['YACCCOM'] = '$YACC $YACCFLAGS -o $TARGET $SOURCES' + env['YACCHFILESUFFIX'] = '.h' + + # Apparently, OS X now creates file.hpp like everybody else + # I have no idea when it changed; it was fixed in 10.4 + #if env['PLATFORM'] == 'darwin': + # # Bison on Mac OS X just appends ".h" to the generated target .cc + # # or .cpp file name. Hooray for delayed expansion of variables. + # env['YACCHXXFILESUFFIX'] = '${TARGET.suffix}.h' + #else: + # env['YACCHXXFILESUFFIX'] = '.hpp' + env['YACCHXXFILESUFFIX'] = '.hpp' + + env['YACCVCGFILESUFFIX'] = '.vcg' + +def exists(env): + return env.Detect(['bison', 'yacc']) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Tool/zip.py b/scons/scons-local-2.3.0/SCons/Tool/zip.py new file mode 100644 index 000000000..86ae55e16 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Tool/zip.py @@ -0,0 +1,99 @@ +"""SCons.Tool.zip + +Tool-specific initialization for zip. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Tool/zip.py 2013/03/03 09:48:35 garyo" + +import os.path + +import SCons.Builder +import SCons.Defaults +import SCons.Node.FS +import SCons.Util + +try: + import zipfile + internal_zip = 1 +except ImportError: + internal_zip = 0 + +if internal_zip: + zipcompression = zipfile.ZIP_DEFLATED + def zip(target, source, env): + compression = env.get('ZIPCOMPRESSION', 0) + zf = zipfile.ZipFile(str(target[0]), 'w', compression) + for s in source: + if s.isdir(): + for dirpath, dirnames, filenames in os.walk(str(s)): + for fname in filenames: + path = os.path.join(dirpath, fname) + if os.path.isfile(path): + zf.write(path) + else: + zf.write(str(s)) + zf.close() +else: + zipcompression = 0 + zip = "$ZIP $ZIPFLAGS ${TARGET.abspath} $SOURCES" + + +zipAction = SCons.Action.Action(zip, varlist=['ZIPCOMPRESSION']) + +ZipBuilder = SCons.Builder.Builder(action = SCons.Action.Action('$ZIPCOM', '$ZIPCOMSTR'), + source_factory = SCons.Node.FS.Entry, + source_scanner = SCons.Defaults.DirScanner, + suffix = '$ZIPSUFFIX', + multi = 1) + + +def generate(env): + """Add Builders and construction variables for zip to an Environment.""" + try: + bld = env['BUILDERS']['Zip'] + except KeyError: + bld = ZipBuilder + env['BUILDERS']['Zip'] = bld + + env['ZIP'] = 'zip' + env['ZIPFLAGS'] = SCons.Util.CLVar('') + env['ZIPCOM'] = zipAction + env['ZIPCOMPRESSION'] = zipcompression + env['ZIPSUFFIX'] = '.zip' + +def exists(env): + return internal_zip or env.Detect('zip') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Util.py b/scons/scons-local-2.3.0/SCons/Util.py new file mode 100644 index 000000000..9f66ce099 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Util.py @@ -0,0 +1,1492 @@ +"""SCons.Util + +Various utility functions go here. +""" +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Util.py 2013/03/03 09:48:35 garyo" + +import os +import sys +import copy +import re +import types + +from collections import UserDict, UserList, UserString + +# Don't "from types import ..." these because we need to get at the +# types module later to look for UnicodeType. +InstanceType = types.InstanceType +MethodType = types.MethodType +FunctionType = types.FunctionType +try: unicode +except NameError: UnicodeType = None +else: UnicodeType = unicode + +def dictify(keys, values, result={}): + for k, v in zip(keys, values): + result[k] = v + return result + +_altsep = os.altsep +if _altsep is None and sys.platform == 'win32': + # My ActivePython 2.0.1 doesn't set os.altsep! What gives? + _altsep = '/' +if _altsep: + def rightmost_separator(path, sep): + return max(path.rfind(sep), path.rfind(_altsep)) +else: + def rightmost_separator(path, sep): + return path.rfind(sep) + +# First two from the Python Cookbook, just for completeness. +# (Yeah, yeah, YAGNI...) +def containsAny(str, set): + """Check whether sequence str contains ANY of the items in set.""" + for c in set: + if c in str: return 1 + return 0 + +def containsAll(str, set): + """Check whether sequence str contains ALL of the items in set.""" + for c in set: + if c not in str: return 0 + return 1 + +def containsOnly(str, set): + """Check whether sequence str contains ONLY items in set.""" + for c in str: + if c not in set: return 0 + return 1 + +def splitext(path): + "Same as os.path.splitext() but faster." + sep = rightmost_separator(path, os.sep) + dot = path.rfind('.') + # An ext is only real if it has at least one non-digit char + if dot > sep and not containsOnly(path[dot:], "0123456789."): + return path[:dot],path[dot:] + else: + return path,"" + +def updrive(path): + """ + Make the drive letter (if any) upper case. + This is useful because Windows is inconsitent on the case + of the drive letter, which can cause inconsistencies when + calculating command signatures. + """ + drive, rest = os.path.splitdrive(path) + if drive: + path = drive.upper() + rest + return path + +class NodeList(UserList): + """This class is almost exactly like a regular list of Nodes + (actually it can hold any object), with one important difference. + If you try to get an attribute from this list, it will return that + attribute from every item in the list. For example: + + >>> someList = NodeList([ ' foo ', ' bar ' ]) + >>> someList.strip() + [ 'foo', 'bar' ] + """ + def __nonzero__(self): + return len(self.data) != 0 + + def __str__(self): + return ' '.join(map(str, self.data)) + + def __iter__(self): + return iter(self.data) + + def __call__(self, *args, **kwargs): + result = [x(*args, **kwargs) for x in self.data] + return self.__class__(result) + + def __getattr__(self, name): + result = [getattr(x, name) for x in self.data] + return self.__class__(result) + + +_get_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[_a-zA-Z]\w*})$') + +def get_environment_var(varstr): + """Given a string, first determine if it looks like a reference + to a single environment variable, like "$FOO" or "${FOO}". + If so, return that variable with no decorations ("FOO"). + If not, return None.""" + mo=_get_env_var.match(to_String(varstr)) + if mo: + var = mo.group(1) + if var[0] == '{': + return var[1:-1] + else: + return var + else: + return None + +class DisplayEngine(object): + print_it = True + def __call__(self, text, append_newline=1): + if not self.print_it: + return + if append_newline: text = text + '\n' + try: + sys.stdout.write(unicode(text)) + except IOError: + # Stdout might be connected to a pipe that has been closed + # by now. The most likely reason for the pipe being closed + # is that the user has press ctrl-c. It this is the case, + # then SCons is currently shutdown. We therefore ignore + # IOError's here so that SCons can continue and shutdown + # properly so that the .sconsign is correctly written + # before SCons exits. + pass + + def set_mode(self, mode): + self.print_it = mode + +def render_tree(root, child_func, prune=0, margin=[0], visited={}): + """ + Render a tree of nodes into an ASCII tree view. + root - the root node of the tree + child_func - the function called to get the children of a node + prune - don't visit the same node twice + margin - the format of the left margin to use for children of root. + 1 results in a pipe, and 0 results in no pipe. + visited - a dictionary of visited nodes in the current branch if not prune, + or in the whole tree if prune. + """ + + rname = str(root) + + children = child_func(root) + retval = "" + for pipe in margin[:-1]: + if pipe: + retval = retval + "| " + else: + retval = retval + " " + + if rname in visited: + return retval + "+-[" + rname + "]\n" + + retval = retval + "+-" + rname + "\n" + if not prune: + visited = copy.copy(visited) + visited[rname] = 1 + + for i in range(len(children)): + margin.append(i 0 + last = t[0] + lasti = i = 1 + while i < n: + if t[i] != last: + t[lasti] = last = t[i] + lasti = lasti + 1 + i = i + 1 + return t[:lasti] + del t + + # Brute force is all that's left. + u = [] + for x in s: + if x not in u: + u.append(x) + return u + + + +# From Alex Martelli, +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560 +# ASPN: Python Cookbook: Remove duplicates from a sequence +# First comment, dated 2001/10/13. +# (Also in the printed Python Cookbook.) + +def uniquer(seq, idfun=None): + if idfun is None: + def idfun(x): return x + seen = {} + result = [] + for item in seq: + marker = idfun(item) + # in old Python versions: + # if seen.has_key(marker) + # but in new ones: + if marker in seen: continue + seen[marker] = 1 + result.append(item) + return result + +# A more efficient implementation of Alex's uniquer(), this avoids the +# idfun() argument and function-call overhead by assuming that all +# items in the sequence are hashable. + +def uniquer_hashables(seq): + seen = {} + result = [] + for item in seq: + #if not item in seen: + if item not in seen: + seen[item] = 1 + result.append(item) + return result + + + +# Much of the logic here was originally based on recipe 4.9 from the +# Python CookBook, but we had to dumb it way down for Python 1.5.2. +class LogicalLines(object): + + def __init__(self, fileobj): + self.fileobj = fileobj + + def readline(self): + result = [] + while True: + line = self.fileobj.readline() + if not line: + break + if line[-2:] == '\\\n': + result.append(line[:-2]) + else: + result.append(line) + break + return ''.join(result) + + def readlines(self): + result = [] + while True: + line = self.readline() + if not line: + break + result.append(line) + return result + + + +class UniqueList(UserList): + def __init__(self, seq = []): + UserList.__init__(self, seq) + self.unique = True + def __make_unique(self): + if not self.unique: + self.data = uniquer_hashables(self.data) + self.unique = True + def __lt__(self, other): + self.__make_unique() + return UserList.__lt__(self, other) + def __le__(self, other): + self.__make_unique() + return UserList.__le__(self, other) + def __eq__(self, other): + self.__make_unique() + return UserList.__eq__(self, other) + def __ne__(self, other): + self.__make_unique() + return UserList.__ne__(self, other) + def __gt__(self, other): + self.__make_unique() + return UserList.__gt__(self, other) + def __ge__(self, other): + self.__make_unique() + return UserList.__ge__(self, other) + def __cmp__(self, other): + self.__make_unique() + return UserList.__cmp__(self, other) + def __len__(self): + self.__make_unique() + return UserList.__len__(self) + def __getitem__(self, i): + self.__make_unique() + return UserList.__getitem__(self, i) + def __setitem__(self, i, item): + UserList.__setitem__(self, i, item) + self.unique = False + def __getslice__(self, i, j): + self.__make_unique() + return UserList.__getslice__(self, i, j) + def __setslice__(self, i, j, other): + UserList.__setslice__(self, i, j, other) + self.unique = False + def __add__(self, other): + result = UserList.__add__(self, other) + result.unique = False + return result + def __radd__(self, other): + result = UserList.__radd__(self, other) + result.unique = False + return result + def __iadd__(self, other): + result = UserList.__iadd__(self, other) + result.unique = False + return result + def __mul__(self, other): + result = UserList.__mul__(self, other) + result.unique = False + return result + def __rmul__(self, other): + result = UserList.__rmul__(self, other) + result.unique = False + return result + def __imul__(self, other): + result = UserList.__imul__(self, other) + result.unique = False + return result + def append(self, item): + UserList.append(self, item) + self.unique = False + def insert(self, i): + UserList.insert(self, i) + self.unique = False + def count(self, item): + self.__make_unique() + return UserList.count(self, item) + def index(self, item): + self.__make_unique() + return UserList.index(self, item) + def reverse(self): + self.__make_unique() + UserList.reverse(self) + def sort(self, *args, **kwds): + self.__make_unique() + return UserList.sort(self, *args, **kwds) + def extend(self, other): + UserList.extend(self, other) + self.unique = False + + +class Unbuffered(object): + """ + A proxy class that wraps a file object, flushing after every write, + and delegating everything else to the wrapped object. + """ + def __init__(self, file): + self.file = file + self.softspace = 0 ## backward compatibility; not supported in Py3k + def write(self, arg): + try: + self.file.write(arg) + self.file.flush() + except IOError: + # Stdout might be connected to a pipe that has been closed + # by now. The most likely reason for the pipe being closed + # is that the user has press ctrl-c. It this is the case, + # then SCons is currently shutdown. We therefore ignore + # IOError's here so that SCons can continue and shutdown + # properly so that the .sconsign is correctly written + # before SCons exits. + pass + def __getattr__(self, attr): + return getattr(self.file, attr) + +def make_path_relative(path): + """ makes an absolute path name to a relative pathname. + """ + if os.path.isabs(path): + drive_s,path = os.path.splitdrive(path) + + import re + if not drive_s: + path=re.compile("/*(.*)").findall(path)[0] + else: + path=path[1:] + + assert( not os.path.isabs( path ) ), path + return path + + + +# The original idea for AddMethod() and RenameFunction() come from the +# following post to the ActiveState Python Cookbook: +# +# ASPN: Python Cookbook : Install bound methods in an instance +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/223613 +# +# That code was a little fragile, though, so the following changes +# have been wrung on it: +# +# * Switched the installmethod() "object" and "function" arguments, +# so the order reflects that the left-hand side is the thing being +# "assigned to" and the right-hand side is the value being assigned. +# +# * Changed explicit type-checking to the "try: klass = object.__class__" +# block in installmethod() below so that it still works with the +# old-style classes that SCons uses. +# +# * Replaced the by-hand creation of methods and functions with use of +# the "new" module, as alluded to in Alex Martelli's response to the +# following Cookbook post: +# +# ASPN: Python Cookbook : Dynamically added methods to a class +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732 + +def AddMethod(obj, function, name=None): + """ + Adds either a bound method to an instance or an unbound method to + a class. If name is ommited the name of the specified function + is used by default. + Example: + a = A() + def f(self, x, y): + self.z = x + y + AddMethod(f, A, "add") + a.add(2, 4) + print a.z + AddMethod(lambda self, i: self.l[i], a, "listIndex") + print a.listIndex(5) + """ + if name is None: + name = function.func_name + else: + function = RenameFunction(function, name) + + if hasattr(obj, '__class__') and obj.__class__ is not type: + # "obj" is an instance, so it gets a bound method. + setattr(obj, name, MethodType(function, obj, obj.__class__)) + else: + # "obj" is a class, so it gets an unbound method. + setattr(obj, name, MethodType(function, None, obj)) + +def RenameFunction(function, name): + """ + Returns a function identical to the specified function, but with + the specified name. + """ + return FunctionType(function.func_code, + function.func_globals, + name, + function.func_defaults) + + +md5 = False +def MD5signature(s): + return str(s) + +def MD5filesignature(fname, chunksize=65536): + f = open(fname, "rb") + result = f.read() + f.close() + return result + +try: + import hashlib +except ImportError: + pass +else: + if hasattr(hashlib, 'md5'): + md5 = True + def MD5signature(s): + m = hashlib.md5() + m.update(str(s)) + return m.hexdigest() + + def MD5filesignature(fname, chunksize=65536): + m = hashlib.md5() + f = open(fname, "rb") + while True: + blck = f.read(chunksize) + if not blck: + break + m.update(str(blck)) + f.close() + return m.hexdigest() + +def MD5collect(signatures): + """ + Collects a list of signatures into an aggregate signature. + + signatures - a list of signatures + returns - the aggregate signature + """ + if len(signatures) == 1: + return signatures[0] + else: + return MD5signature(', '.join(signatures)) + + + +def silent_intern(x): + """ + Perform sys.intern() on the passed argument and return the result. + If the input is ineligible (e.g. a unicode string) the original argument is + returned and no exception is thrown. + """ + try: + return sys.intern(x) + except TypeError: + return x + + + +# From Dinu C. Gherman, +# Python Cookbook, second edition, recipe 6.17, p. 277. +# Also: +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/68205 +# ASPN: Python Cookbook: Null Object Design Pattern + +#TODO??? class Null(object): +class Null(object): + """ Null objects always and reliably "do nothing." """ + def __new__(cls, *args, **kwargs): + if not '_instance' in vars(cls): + cls._instance = super(Null, cls).__new__(cls, *args, **kwargs) + return cls._instance + def __init__(self, *args, **kwargs): + pass + def __call__(self, *args, **kwargs): + return self + def __repr__(self): + return "Null(0x%08X)" % id(self) + def __nonzero__(self): + return False + def __getattr__(self, name): + return self + def __setattr__(self, name, value): + return self + def __delattr__(self, name): + return self + +class NullSeq(Null): + def __len__(self): + return 0 + def __iter__(self): + return iter(()) + def __getitem__(self, i): + return self + def __delitem__(self, i): + return self + def __setitem__(self, i, v): + return self + + +del __revision__ + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Variables/BoolVariable.py b/scons/scons-local-2.3.0/SCons/Variables/BoolVariable.py new file mode 100644 index 000000000..cdfe3671d --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Variables/BoolVariable.py @@ -0,0 +1,89 @@ +"""engine.SCons.Variables.BoolVariable + +This file defines the option type for SCons implementing true/false values. + +Usage example: + + opts = Variables() + opts.Add(BoolVariable('embedded', 'build for an embedded system', 0)) + ... + if env['embedded'] == 1: + ... +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Variables/BoolVariable.py 2013/03/03 09:48:35 garyo" + +__all__ = ['BoolVariable',] + +import SCons.Errors + +__true_strings = ('y', 'yes', 'true', 't', '1', 'on' , 'all' ) +__false_strings = ('n', 'no', 'false', 'f', '0', 'off', 'none') + + +def _text2bool(val): + """ + Converts strings to True/False depending on the 'truth' expressed by + the string. If the string can't be converted, the original value + will be returned. + + See '__true_strings' and '__false_strings' for values considered + 'true' or 'false respectivly. + + This is usable as 'converter' for SCons' Variables. + """ + lval = val.lower() + if lval in __true_strings: return True + if lval in __false_strings: return False + raise ValueError("Invalid value for boolean option: %s" % val) + + +def _validator(key, val, env): + """ + Validates the given value to be either '0' or '1'. + + This is usable as 'validator' for SCons' Variables. + """ + if not env[key] in (True, False): + raise SCons.Errors.UserError( + 'Invalid value for boolean option %s: %s' % (key, env[key])) + + +def BoolVariable(key, help, default): + """ + The input parameters describe a boolen option, thus they are + returned with the correct converter and validator appended. The + 'help' text will by appended by '(yes|no) to show the valid + valued. The result is usable for input to opts.Add(). + """ + return (key, '%s (yes|no)' % help, default, + _validator, _text2bool) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Variables/EnumVariable.py b/scons/scons-local-2.3.0/SCons/Variables/EnumVariable.py new file mode 100644 index 000000000..9e2970f6b --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Variables/EnumVariable.py @@ -0,0 +1,103 @@ +"""engine.SCons.Variables.EnumVariable + +This file defines the option type for SCons allowing only specified +input-values. + +Usage example: + + opts = Variables() + opts.Add(EnumVariable('debug', 'debug output and symbols', 'no', + allowed_values=('yes', 'no', 'full'), + map={}, ignorecase=2)) + ... + if env['debug'] == 'full': + ... +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Variables/EnumVariable.py 2013/03/03 09:48:35 garyo" + +__all__ = ['EnumVariable',] + + +import SCons.Errors + +def _validator(key, val, env, vals): + if not val in vals: + raise SCons.Errors.UserError( + 'Invalid value for option %s: %s. Valid values are: %s' % (key, val, vals)) + + +def EnumVariable(key, help, default, allowed_values, map={}, ignorecase=0): + """ + The input parameters describe a option with only certain values + allowed. They are returned with an appropriate converter and + validator appended. The result is usable for input to + Variables.Add(). + + 'key' and 'default' are the values to be passed on to Variables.Add(). + + 'help' will be appended by the allowed values automatically + + 'allowed_values' is a list of strings, which are allowed as values + for this option. + + The 'map'-dictionary may be used for converting the input value + into canonical values (eg. for aliases). + + 'ignorecase' defines the behaviour of the validator: + + If ignorecase == 0, the validator/converter are case-sensitive. + If ignorecase == 1, the validator/converter are case-insensitive. + If ignorecase == 2, the validator/converter is case-insensitive and + the converted value will always be lower-case. + + The 'validator' tests whether the value is in the list of allowed + values. The 'converter' converts input values according to the + given 'map'-dictionary (unmapped input values are returned + unchanged). + """ + help = '%s (%s)' % (help, '|'.join(allowed_values)) + # define validator + if ignorecase >= 1: + validator = lambda key, val, env: \ + _validator(key, val.lower(), env, allowed_values) + else: + validator = lambda key, val, env: \ + _validator(key, val, env, allowed_values) + # define converter + if ignorecase == 2: + converter = lambda val: map.get(val.lower(), val).lower() + elif ignorecase == 1: + converter = lambda val: map.get(val.lower(), val) + else: + converter = lambda val: map.get(val, val) + return (key, help, default, validator, converter) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Variables/ListVariable.py b/scons/scons-local-2.3.0/SCons/Variables/ListVariable.py new file mode 100644 index 000000000..735d5257b --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Variables/ListVariable.py @@ -0,0 +1,135 @@ +"""engine.SCons.Variables.ListVariable + +This file defines the option type for SCons implementing 'lists'. + +A 'list' option may either be 'all', 'none' or a list of names +separated by comma. After the option has been processed, the option +value holds either the named list elements, all list elemens or no +list elements at all. + +Usage example: + + list_of_libs = Split('x11 gl qt ical') + + opts = Variables() + opts.Add(ListVariable('shared', + 'libraries to build as shared libraries', + 'all', + elems = list_of_libs)) + ... + for lib in list_of_libs: + if lib in env['shared']: + env.SharedObject(...) + else: + env.Object(...) +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Variables/ListVariable.py 2013/03/03 09:48:35 garyo" + +# Know Bug: This should behave like a Set-Type, but does not really, +# since elements can occur twice. + +__all__ = ['ListVariable',] + +import collections + +import SCons.Util + + +class _ListVariable(collections.UserList): + def __init__(self, initlist=[], allowedElems=[]): + collections.UserList.__init__(self, [_f for _f in initlist if _f]) + self.allowedElems = sorted(allowedElems) + + def __cmp__(self, other): + raise NotImplementedError + def __eq__(self, other): + raise NotImplementedError + def __ge__(self, other): + raise NotImplementedError + def __gt__(self, other): + raise NotImplementedError + def __le__(self, other): + raise NotImplementedError + def __lt__(self, other): + raise NotImplementedError + def __str__(self): + if len(self) == 0: + return 'none' + self.data.sort() + if self.data == self.allowedElems: + return 'all' + else: + return ','.join(self) + def prepare_to_store(self): + return self.__str__() + +def _converter(val, allowedElems, mapdict): + """ + """ + if val == 'none': + val = [] + elif val == 'all': + val = allowedElems + else: + val = [_f for _f in val.split(',') if _f] + val = [mapdict.get(v, v) for v in val] + notAllowed = [v for v in val if not v in allowedElems] + if notAllowed: + raise ValueError("Invalid value(s) for option: %s" % + ','.join(notAllowed)) + return _ListVariable(val, allowedElems) + + +## def _validator(key, val, env): +## """ +## """ +## # todo: write validater for pgk list +## return 1 + + +def ListVariable(key, help, default, names, map={}): + """ + The input parameters describe a 'package list' option, thus they + are returned with the correct converter and validater appended. The + result is usable for input to opts.Add() . + + A 'package list' option may either be 'all', 'none' or a list of + package names (separated by space). + """ + names_str = 'allowed names: %s' % ' '.join(names) + if SCons.Util.is_List(default): + default = ','.join(default) + help = '\n '.join( + (help, '(all|none|comma-separated list of names)', names_str)) + return (key, help, default, + None, #_validator, + lambda val: _converter(val, names, map)) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Variables/PackageVariable.py b/scons/scons-local-2.3.0/SCons/Variables/PackageVariable.py new file mode 100644 index 000000000..665692c6c --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Variables/PackageVariable.py @@ -0,0 +1,106 @@ +"""engine.SCons.Variables.PackageVariable + +This file defines the option type for SCons implementing 'package +activation'. + +To be used whenever a 'package' may be enabled/disabled and the +package path may be specified. + +Usage example: + + Examples: + x11=no (disables X11 support) + x11=yes (will search for the package installation dir) + x11=/usr/local/X11 (will check this path for existance) + + To replace autoconf's --with-xxx=yyy + + opts = Variables() + opts.Add(PackageVariable('x11', + 'use X11 installed here (yes = search some places', + 'yes')) + ... + if env['x11'] == True: + dir = ... search X11 in some standard places ... + env['x11'] = dir + if env['x11']: + ... build with x11 ... +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Variables/PackageVariable.py 2013/03/03 09:48:35 garyo" + +__all__ = ['PackageVariable',] + +import SCons.Errors + +__enable_strings = ('1', 'yes', 'true', 'on', 'enable', 'search') +__disable_strings = ('0', 'no', 'false', 'off', 'disable') + +def _converter(val): + """ + """ + lval = val.lower() + if lval in __enable_strings: return True + if lval in __disable_strings: return False + #raise ValueError("Invalid value for boolean option: %s" % val) + return val + + +def _validator(key, val, env, searchfunc): + # NB: searchfunc is currenty undocumented and unsupported + """ + """ + # todo: write validator, check for path + import os + if env[key] is True: + if searchfunc: + env[key] = searchfunc(key, val) + elif env[key] and not os.path.exists(val): + raise SCons.Errors.UserError( + 'Path does not exist for option %s: %s' % (key, val)) + + +def PackageVariable(key, help, default, searchfunc=None): + # NB: searchfunc is currenty undocumented and unsupported + """ + The input parameters describe a 'package list' option, thus they + are returned with the correct converter and validator appended. The + result is usable for input to opts.Add() . + + A 'package list' option may either be 'all', 'none' or a list of + package names (seperated by space). + """ + help = '\n '.join( + (help, '( yes | no | /path/to/%s )' % key)) + return (key, help, default, + lambda k, v, e: _validator(k,v,e,searchfunc), + _converter) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Variables/PathVariable.py b/scons/scons-local-2.3.0/SCons/Variables/PathVariable.py new file mode 100644 index 000000000..b79a7606a --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Variables/PathVariable.py @@ -0,0 +1,147 @@ +"""SCons.Variables.PathVariable + +This file defines an option type for SCons implementing path settings. + +To be used whenever a a user-specified path override should be allowed. + +Arguments to PathVariable are: + option-name = name of this option on the command line (e.g. "prefix") + option-help = help string for option + option-dflt = default value for this option + validator = [optional] validator for option value. Predefined + validators are: + + PathAccept -- accepts any path setting; no validation + PathIsDir -- path must be an existing directory + PathIsDirCreate -- path must be a dir; will create + PathIsFile -- path must be a file + PathExists -- path must exist (any type) [default] + + The validator is a function that is called and which + should return True or False to indicate if the path + is valid. The arguments to the validator function + are: (key, val, env). The key is the name of the + option, the val is the path specified for the option, + and the env is the env to which the Otions have been + added. + +Usage example: + + Examples: + prefix=/usr/local + + opts = Variables() + + opts = Variables() + opts.Add(PathVariable('qtdir', + 'where the root of Qt is installed', + qtdir, PathIsDir)) + opts.Add(PathVariable('qt_includes', + 'where the Qt includes are installed', + '$qtdir/includes', PathIsDirCreate)) + opts.Add(PathVariable('qt_libraries', + 'where the Qt library is installed', + '$qtdir/lib')) + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/Variables/PathVariable.py 2013/03/03 09:48:35 garyo" + +__all__ = ['PathVariable',] + +import os +import os.path + +import SCons.Errors + +class _PathVariableClass(object): + + def PathAccept(self, key, val, env): + """Accepts any path, no checking done.""" + pass + + def PathIsDir(self, key, val, env): + """Validator to check if Path is a directory.""" + if not os.path.isdir(val): + if os.path.isfile(val): + m = 'Directory path for option %s is a file: %s' + else: + m = 'Directory path for option %s does not exist: %s' + raise SCons.Errors.UserError(m % (key, val)) + + def PathIsDirCreate(self, key, val, env): + """Validator to check if Path is a directory, + creating it if it does not exist.""" + if os.path.isfile(val): + m = 'Path for option %s is a file, not a directory: %s' + raise SCons.Errors.UserError(m % (key, val)) + if not os.path.isdir(val): + os.makedirs(val) + + def PathIsFile(self, key, val, env): + """validator to check if Path is a file""" + if not os.path.isfile(val): + if os.path.isdir(val): + m = 'File path for option %s is a directory: %s' + else: + m = 'File path for option %s does not exist: %s' + raise SCons.Errors.UserError(m % (key, val)) + + def PathExists(self, key, val, env): + """validator to check if Path exists""" + if not os.path.exists(val): + m = 'Path for option %s does not exist: %s' + raise SCons.Errors.UserError(m % (key, val)) + + def __call__(self, key, help, default, validator=None): + # NB: searchfunc is currenty undocumented and unsupported + """ + The input parameters describe a 'path list' option, thus they + are returned with the correct converter and validator appended. The + result is usable for input to opts.Add() . + + The 'default' option specifies the default path to use if the + user does not specify an override with this option. + + validator is a validator, see this file for examples + """ + if validator is None: + validator = self.PathExists + + if SCons.Util.is_List(key) or SCons.Util.is_Tuple(key): + return (key, '%s ( /path/to/%s )' % (help, key[0]), default, + validator, None) + else: + return (key, '%s ( /path/to/%s )' % (help, key), default, + validator, None) + +PathVariable = _PathVariableClass() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Variables/__init__.py b/scons/scons-local-2.3.0/SCons/Variables/__init__.py new file mode 100644 index 000000000..3d9d8452a --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Variables/__init__.py @@ -0,0 +1,312 @@ +"""engine.SCons.Variables + +This file defines the Variables class that is used to add user-friendly +customizable variables to an SCons build. +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. + +__revision__ = "src/engine/SCons/Variables/__init__.py 2013/03/03 09:48:35 garyo" + +import os.path +import sys + +import SCons.Environment +import SCons.Errors +import SCons.Util +import SCons.Warnings + +from BoolVariable import BoolVariable # okay +from EnumVariable import EnumVariable # okay +from ListVariable import ListVariable # naja +from PackageVariable import PackageVariable # naja +from PathVariable import PathVariable # okay + + +class Variables(object): + instance=None + + """ + Holds all the options, updates the environment with the variables, + and renders the help text. + """ + def __init__(self, files=[], args={}, is_global=1): + """ + files - [optional] List of option configuration files to load + (backward compatibility) If a single string is passed it is + automatically placed in a file list + """ + self.options = [] + self.args = args + if not SCons.Util.is_List(files): + if files: + files = [ files ] + else: + files = [] + self.files = files + self.unknown = {} + + # create the singleton instance + if is_global: + self=Variables.instance + + if not Variables.instance: + Variables.instance=self + + def _do_add(self, key, help="", default=None, validator=None, converter=None): + class Variable(object): + pass + + option = Variable() + + # if we get a list or a tuple, we take the first element as the + # option key and store the remaining in aliases. + if SCons.Util.is_List(key) or SCons.Util.is_Tuple(key): + option.key = key[0] + option.aliases = key[1:] + else: + option.key = key + option.aliases = [ key ] + option.help = help + option.default = default + option.validator = validator + option.converter = converter + + self.options.append(option) + + # options might be added after the 'unknown' dict has been set up, + # so we remove the key and all its aliases from that dict + for alias in list(option.aliases) + [ option.key ]: + if alias in self.unknown: + del self.unknown[alias] + + def keys(self): + """ + Returns the keywords for the options + """ + return [o.key for o in self.options] + + def Add(self, key, help="", default=None, validator=None, converter=None, **kw): + """ + Add an option. + + key - the name of the variable, or a list or tuple of arguments + help - optional help text for the options + default - optional default value + validator - optional function that is called to validate the option's value + Called with (key, value, environment) + converter - optional function that is called to convert the option's value before + putting it in the environment. + """ + + if SCons.Util.is_List(key) or isinstance(key, tuple): + self._do_add(*key) + return + + if not SCons.Util.is_String(key) or \ + not SCons.Environment.is_valid_construction_var(key): + raise SCons.Errors.UserError("Illegal Variables.Add() key `%s'" % str(key)) + + self._do_add(key, help, default, validator, converter) + + def AddVariables(self, *optlist): + """ + Add a list of options. + + Each list element is a tuple/list of arguments to be passed on + to the underlying method for adding options. + + Example: + opt.AddVariables( + ('debug', '', 0), + ('CC', 'The C compiler'), + ('VALIDATE', 'An option for testing validation', 'notset', + validator, None), + ) + """ + for o in optlist: + self._do_add(*o) + + + def Update(self, env, args=None): + """ + Update an environment with the option variables. + + env - the environment to update. + """ + + values = {} + + # first set the defaults: + for option in self.options: + if not option.default is None: + values[option.key] = option.default + + # next set the value specified in the options file + for filename in self.files: + if os.path.exists(filename): + dir = os.path.split(os.path.abspath(filename))[0] + if dir: + sys.path.insert(0, dir) + try: + values['__name__'] = filename + exec open(filename, 'rU').read() in {}, values + finally: + if dir: + del sys.path[0] + del values['__name__'] + + # set the values specified on the command line + if args is None: + args = self.args + + for arg, value in args.items(): + added = False + for option in self.options: + if arg in list(option.aliases) + [ option.key ]: + values[option.key] = value + added = True + if not added: + self.unknown[arg] = value + + # put the variables in the environment: + # (don't copy over variables that are not declared as options) + for option in self.options: + try: + env[option.key] = values[option.key] + except KeyError: + pass + + # Call the convert functions: + for option in self.options: + if option.converter and option.key in values: + value = env.subst('${%s}'%option.key) + try: + try: + env[option.key] = option.converter(value) + except TypeError: + env[option.key] = option.converter(value, env) + except ValueError, x: + raise SCons.Errors.UserError('Error converting option: %s\n%s'%(option.key, x)) + + + # Finally validate the values: + for option in self.options: + if option.validator and option.key in values: + option.validator(option.key, env.subst('${%s}'%option.key), env) + + def UnknownVariables(self): + """ + Returns any options in the specified arguments lists that + were not known, declared options in this object. + """ + return self.unknown + + def Save(self, filename, env): + """ + Saves all the options in the given file. This file can + then be used to load the options next run. This can be used + to create an option cache file. + + filename - Name of the file to save into + env - the environment get the option values from + """ + + # Create the file and write out the header + try: + fh = open(filename, 'w') + + try: + # Make an assignment in the file for each option + # within the environment that was assigned a value + # other than the default. + for option in self.options: + try: + value = env[option.key] + try: + prepare = value.prepare_to_store + except AttributeError: + try: + eval(repr(value)) + except KeyboardInterrupt: + raise + except: + # Convert stuff that has a repr() that + # cannot be evaluated into a string + value = SCons.Util.to_String(value) + else: + value = prepare() + + defaultVal = env.subst(SCons.Util.to_String(option.default)) + if option.converter: + defaultVal = option.converter(defaultVal) + + if str(env.subst('${%s}' % option.key)) != str(defaultVal): + fh.write('%s = %s\n' % (option.key, repr(value))) + except KeyError: + pass + finally: + fh.close() + + except IOError, x: + raise SCons.Errors.UserError('Error writing options to file: %s\n%s' % (filename, x)) + + def GenerateHelpText(self, env, sort=None): + """ + Generate the help text for the options. + + env - an environment that is used to get the current values + of the options. + """ + + if sort: + options = sorted(self.options, key=lambda x: x.key) + else: + options = self.options + + def format(opt, self=self, env=env): + if opt.key in env: + actual = env.subst('${%s}' % opt.key) + else: + actual = None + return self.FormatVariableHelpText(env, opt.key, opt.help, opt.default, actual, opt.aliases) + lines = [_f for _f in map(format, options) if _f] + + return ''.join(lines) + + format = '\n%s: %s\n default: %s\n actual: %s\n' + format_ = '\n%s: %s\n default: %s\n actual: %s\n aliases: %s\n' + + def FormatVariableHelpText(self, env, key, help, default, actual, aliases=[]): + # Don't display the key name itself as an alias. + aliases = [a for a in aliases if a != key] + if len(aliases)==0: + return self.format % (key, help, default, actual) + else: + return self.format_ % (key, help, default, actual, aliases) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/Warnings.py b/scons/scons-local-2.3.0/SCons/Warnings.py new file mode 100644 index 000000000..d7cadc25a --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/Warnings.py @@ -0,0 +1,246 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +"""SCons.Warnings + +This file implements the warnings framework for SCons. + +""" + +__revision__ = "src/engine/SCons/Warnings.py 2013/03/03 09:48:35 garyo" + +import sys + +import SCons.Errors + +class Warning(SCons.Errors.UserError): + pass + +class WarningOnByDefault(Warning): + pass + + +# NOTE: If you add a new warning class, add it to the man page, too! + +class CacheWriteErrorWarning(Warning): + pass + +class CorruptSConsignWarning(WarningOnByDefault): + pass + +class DependencyWarning(Warning): + pass + +class DuplicateEnvironmentWarning(WarningOnByDefault): + pass + +class FutureReservedVariableWarning(WarningOnByDefault): + pass + +class LinkWarning(WarningOnByDefault): + pass + +class MisleadingKeywordsWarning(WarningOnByDefault): + pass + +class MissingSConscriptWarning(WarningOnByDefault): + pass + +class NoMD5ModuleWarning(WarningOnByDefault): + pass + +class NoMetaclassSupportWarning(WarningOnByDefault): + pass + +class NoObjectCountWarning(WarningOnByDefault): + pass + +class NoParallelSupportWarning(WarningOnByDefault): + pass + +class ReservedVariableWarning(WarningOnByDefault): + pass + +class StackSizeWarning(WarningOnByDefault): + pass + +class VisualCMissingWarning(WarningOnByDefault): + pass + +# Used when MSVC_VERSION and MSVS_VERSION do not point to the +# same version (MSVS_VERSION is deprecated) +class VisualVersionMismatch(WarningOnByDefault): + pass + +class VisualStudioMissingWarning(Warning): + pass + +class FortranCxxMixWarning(LinkWarning): + pass + + +# Deprecation warnings + +class FutureDeprecatedWarning(Warning): + pass + +class DeprecatedWarning(Warning): + pass + +class MandatoryDeprecatedWarning(DeprecatedWarning): + pass + + +# Special case; base always stays DeprecatedWarning +class PythonVersionWarning(DeprecatedWarning): + pass + +class DeprecatedSourceCodeWarning(FutureDeprecatedWarning): + pass + +class DeprecatedBuildDirWarning(DeprecatedWarning): + pass + +class TaskmasterNeedsExecuteWarning(DeprecatedWarning): + pass + +class DeprecatedCopyWarning(MandatoryDeprecatedWarning): + pass + +class DeprecatedOptionsWarning(MandatoryDeprecatedWarning): + pass + +class DeprecatedSourceSignaturesWarning(MandatoryDeprecatedWarning): + pass + +class DeprecatedTargetSignaturesWarning(MandatoryDeprecatedWarning): + pass + +class DeprecatedDebugOptionsWarning(MandatoryDeprecatedWarning): + pass + +class DeprecatedSigModuleWarning(MandatoryDeprecatedWarning): + pass + +class DeprecatedBuilderKeywordsWarning(MandatoryDeprecatedWarning): + pass + + +# The below is a list of 2-tuples. The first element is a class object. +# The second element is true if that class is enabled, false if it is disabled. +_enabled = [] + +# If set, raise the warning as an exception +_warningAsException = 0 + +# If not None, a function to call with the warning +_warningOut = None + +def suppressWarningClass(clazz): + """Suppresses all warnings that are of type clazz or + derived from clazz.""" + _enabled.insert(0, (clazz, 0)) + +def enableWarningClass(clazz): + """Enables all warnings that are of type clazz or + derived from clazz.""" + _enabled.insert(0, (clazz, 1)) + +def warningAsException(flag=1): + """Turn warnings into exceptions. Returns the old value of the flag.""" + global _warningAsException + old = _warningAsException + _warningAsException = flag + return old + +def warn(clazz, *args): + global _enabled, _warningAsException, _warningOut + + warning = clazz(args) + for clazz, flag in _enabled: + if isinstance(warning, clazz): + if flag: + if _warningAsException: + raise warning + + if _warningOut: + _warningOut(warning) + break + +def process_warn_strings(arguments): + """Process string specifications of enabling/disabling warnings, + as passed to the --warn option or the SetOption('warn') function. + + + An argument to this option should be of the form + or no-. The warning class is munged in order + to get an actual class name from the classes above, which we + need to pass to the {enable,disable}WarningClass() functions. + The supplied is split on hyphens, each element + is capitalized, then smushed back together. Then the string + "Warning" is appended to get the class name. + + For example, 'deprecated' will enable the DeprecatedWarning + class. 'no-dependency' will disable the DependencyWarning class. + + As a special case, --warn=all and --warn=no-all will enable or + disable (respectively) the base Warning class of all warnings. + + """ + + def _capitalize(s): + if s[:5] == "scons": + return "SCons" + s[5:] + else: + return s.capitalize() + + for arg in arguments: + + elems = arg.lower().split('-') + enable = 1 + if elems[0] == 'no': + enable = 0 + del elems[0] + + if len(elems) == 1 and elems[0] == 'all': + class_name = "Warning" + else: + class_name = ''.join(map(_capitalize, elems)) + "Warning" + try: + clazz = globals()[class_name] + except KeyError: + sys.stderr.write("No warning type: '%s'\n" % arg) + else: + if enable: + enableWarningClass(clazz) + elif issubclass(clazz, MandatoryDeprecatedWarning): + fmt = "Can not disable mandataory warning: '%s'\n" + sys.stderr.write(fmt % arg) + else: + suppressWarningClass(clazz) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/__init__.py b/scons/scons-local-2.3.0/SCons/__init__.py new file mode 100644 index 000000000..d1a369f4f --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/__init__.py @@ -0,0 +1,49 @@ +"""SCons + +The main package for the SCons software construction utility. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/__init__.py 2013/03/03 09:48:35 garyo" + +__version__ = "2.3.0" + +__build__ = "" + +__buildsys__ = "reepicheep" + +__date__ = "2013/03/03 09:48:35" + +__developer__ = "garyo" + +# make sure compatibility is always in place +import SCons.compat + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/compat/__init__.py b/scons/scons-local-2.3.0/SCons/compat/__init__.py new file mode 100644 index 000000000..56f374259 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/compat/__init__.py @@ -0,0 +1,237 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__doc__ = """ +SCons compatibility package for old Python versions + +This subpackage holds modules that provide backwards-compatible +implementations of various things that we'd like to use in SCons but which +only show up in later versions of Python than the early, old version(s) +we still support. + +Other code will not generally reference things in this package through +the SCons.compat namespace. The modules included here add things to +the builtins namespace or the global module list so that the rest +of our code can use the objects and names imported here regardless of +Python version. + +Simply enough, things that go in the builtins name space come from +our _scons_builtins module. + +The rest of the things here will be in individual compatibility modules +that are either: 1) suitably modified copies of the future modules that +we want to use; or 2) backwards compatible re-implementations of the +specific portions of a future module's API that we want to use. + +GENERAL WARNINGS: Implementations of functions in the SCons.compat +modules are *NOT* guaranteed to be fully compliant with these functions in +later versions of Python. We are only concerned with adding functionality +that we actually use in SCons, so be wary if you lift this code for +other uses. (That said, making these more nearly the same as later, +official versions is still a desirable goal, we just don't need to be +obsessive about it.) + +We name the compatibility modules with an initial '_scons_' (for example, +_scons_subprocess.py is our compatibility module for subprocess) so +that we can still try to import the real module name and fall back to +our compatibility module if we get an ImportError. The import_as() +function defined below loads the module as the "real" name (without the +'_scons'), after which all of the "import {module}" statements in the +rest of our code will find our pre-loaded compatibility module. +""" + +__revision__ = "src/engine/SCons/compat/__init__.py 2013/03/03 09:48:35 garyo" + +import os +import sys +import imp # Use the "imp" module to protect imports from fixers. + +def import_as(module, name): + """ + Imports the specified module (from our local directory) as the + specified name, returning the loaded module object. + """ + dir = os.path.split(__file__)[0] + return imp.load_module(name, *imp.find_module(module, [dir])) + +def rename_module(new, old): + """ + Attempts to import the old module and load it under the new name. + Used for purely cosmetic name changes in Python 3.x. + """ + try: + sys.modules[new] = imp.load_module(old, *imp.find_module(old)) + return True + except ImportError: + return False + + +rename_module('builtins', '__builtin__') +import _scons_builtins + + +try: + import hashlib +except ImportError: + # Pre-2.5 Python has no hashlib module. + try: + import_as('_scons_hashlib', 'hashlib') + except ImportError: + # If we failed importing our compatibility module, it probably + # means this version of Python has no md5 module. Don't do + # anything and let the higher layer discover this fact, so it + # can fall back to using timestamp. + pass + +try: + set +except NameError: + # Pre-2.4 Python has no native set type + import_as('_scons_sets', 'sets') + import builtins, sets + builtins.set = sets.Set + + +try: + import collections +except ImportError: + # Pre-2.4 Python has no collections module. + import_as('_scons_collections', 'collections') +else: + try: + collections.UserDict + except AttributeError: + exec('from UserDict import UserDict as _UserDict') + collections.UserDict = _UserDict + del _UserDict + try: + collections.UserList + except AttributeError: + exec('from UserList import UserList as _UserList') + collections.UserList = _UserList + del _UserList + try: + collections.UserString + except AttributeError: + exec('from UserString import UserString as _UserString') + collections.UserString = _UserString + del _UserString + + +try: + import io +except ImportError: + # Pre-2.6 Python has no io module. + import_as('_scons_io', 'io') + + +try: + os.devnull +except AttributeError: + # Pre-2.4 Python has no os.devnull attribute + _names = sys.builtin_module_names + if 'posix' in _names: + os.devnull = '/dev/null' + elif 'nt' in _names: + os.devnull = 'nul' + os.path.devnull = os.devnull +try: + os.path.lexists +except AttributeError: + # Pre-2.4 Python has no os.path.lexists function + def lexists(path): + return os.path.exists(path) or os.path.islink(path) + os.path.lexists = lexists + + +# When we're using the '-3' option during regression tests, importing +# cPickle gives a warning no matter how it's done, so always use the +# real profile module, whether it's fast or not. +if os.environ.get('SCONS_HORRIBLE_REGRESSION_TEST_HACK') is None: + # Not a regression test with '-3', so try to use faster version. + # In 3.x, 'pickle' automatically loads the fast version if available. + rename_module('pickle', 'cPickle') + + +# In 3.x, 'profile' automatically loads the fast version if available. +rename_module('profile', 'cProfile') + + +# Before Python 3.0, the 'queue' module was named 'Queue'. +rename_module('queue', 'Queue') + + +# Before Python 3.0, the 'winreg' module was named '_winreg' +rename_module('winreg', '_winreg') + + +try: + import subprocess +except ImportError: + # Pre-2.4 Python has no subprocess module. + import_as('_scons_subprocess', 'subprocess') + +try: + sys.intern +except AttributeError: + # Pre-2.6 Python has no sys.intern() function. + import builtins + try: + sys.intern = builtins.intern + except AttributeError: + # Pre-2.x Python has no builtin intern() function. + def intern(x): + return x + sys.intern = intern + del intern +try: + sys.maxsize +except AttributeError: + # Pre-2.6 Python has no sys.maxsize attribute + # Wrapping sys in () is silly, but protects it from 2to3 renames fixer + sys.maxsize = (sys).maxint + + +if os.environ.get('SCONS_HORRIBLE_REGRESSION_TEST_HACK') is not None: + # We can't apply the 'callable' fixer until the floor is 2.6, but the + # '-3' option to Python 2.6 and 2.7 generates almost ten thousand + # warnings. This hack allows us to run regression tests with the '-3' + # option by replacing the callable() built-in function with a hack + # that performs the same function but doesn't generate the warning. + # Note that this hack is ONLY intended to be used for regression + # testing, and should NEVER be used for real runs. + from types import ClassType + def callable(obj): + if hasattr(obj, '__call__'): return True + if isinstance(obj, (ClassType, type)): return True + return False + import builtins + builtins.callable = callable + del callable + + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/compat/_scons_builtins.py b/scons/scons-local-2.3.0/SCons/compat/_scons_builtins.py new file mode 100644 index 000000000..1202d46e0 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/compat/_scons_builtins.py @@ -0,0 +1,107 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +# Portions of the following are derived from the compat.py file in +# Twisted, under the following copyright: +# +# Copyright (c) 2001-2004 Twisted Matrix Laboratories + +__doc__ = """ +Compatibility idioms for builtins names + +This module adds names to the builtins module for things that we want +to use in SCons but which don't show up until later Python versions than +the earliest ones we support. + +This module checks for the following builtins names: + + all() + any() + memoryview() + +Implementations of functions are *NOT* guaranteed to be fully compliant +with these functions in later versions of Python. We are only concerned +with adding functionality that we actually use in SCons, so be wary +if you lift this code for other uses. (That said, making these more +nearly the same as later, official versions is still a desirable goal, +we just don't need to be obsessive about it.) + +If you're looking at this with pydoc and various names don't show up in +the FUNCTIONS or DATA output, that means those names are already built in +to this version of Python and we don't need to add them from this module. +""" + +__revision__ = "src/engine/SCons/compat/_scons_builtins.py 2013/03/03 09:48:35 garyo" + +import builtins + +try: + all +except NameError: + # Pre-2.5 Python has no all() function. + def all(iterable): + """ + Returns True if all elements of the iterable are true. + """ + for element in iterable: + if not element: + return False + return True + builtins.all = all + all = all + +try: + any +except NameError: + # Pre-2.5 Python has no any() function. + def any(iterable): + """ + Returns True if any element of the iterable is true. + """ + for element in iterable: + if element: + return True + return False + builtins.any = any + any = any + +try: + memoryview +except NameError: + # Pre-2.7 doesn't have the memoryview() built-in. + class memoryview(object): + def __init__(self, obj): + # wrapping buffer in () keeps the fixer from changing it + self.obj = (buffer)(obj) + def __getitem__(self, indx): + if isinstance(indx, slice): + return self.obj[indx.start:indx.stop] + else: + return self.obj[indx] + builtins.memoryview = memoryview + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/compat/_scons_collections.py b/scons/scons-local-2.3.0/SCons/compat/_scons_collections.py new file mode 100644 index 000000000..7cf685ccd --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/compat/_scons_collections.py @@ -0,0 +1,45 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__doc__ = """ +collections compatibility module for older (pre-2.4) Python versions + +This does not not NOT (repeat, *NOT*) provide complete collections +functionality. It only wraps the portions of collections functionality +used by SCons, in an interface that looks enough like collections for +our purposes. +""" + +__revision__ = "src/engine/SCons/compat/_scons_collections.py 2013/03/03 09:48:35 garyo" + +# Use exec to hide old names from fixers. +exec("""if True: + from UserDict import UserDict + from UserList import UserList + from UserString import UserString""") + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/compat/_scons_dbm.py b/scons/scons-local-2.3.0/SCons/compat/_scons_dbm.py new file mode 100644 index 000000000..e6df06341 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/compat/_scons_dbm.py @@ -0,0 +1,45 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__doc__ = """ +dbm compatibility module for Python versions that don't have dbm. + +This does not not NOT (repeat, *NOT*) provide complete dbm functionality. +It's just a stub on which to hang just enough pieces of dbm functionality +that the whichdb.whichdb() implementstation in the various 2.X versions of +Python won't blow up even if dbm wasn't compiled in. +""" + +__revision__ = "src/engine/SCons/compat/_scons_dbm.py 2013/03/03 09:48:35 garyo" + +class error(Exception): + pass + +def open(*args, **kw): + raise error() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/compat/_scons_hashlib.py b/scons/scons-local-2.3.0/SCons/compat/_scons_hashlib.py new file mode 100644 index 000000000..d4e30232b --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/compat/_scons_hashlib.py @@ -0,0 +1,76 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__doc__ = """ +hashlib backwards-compatibility module for older (pre-2.5) Python versions + +This does not not NOT (repeat, *NOT*) provide complete hashlib +functionality. It only wraps the portions of MD5 functionality used +by SCons, in an interface that looks like hashlib (or enough for our +purposes, anyway). In fact, this module will raise an ImportError if +the underlying md5 module isn't available. +""" + +__revision__ = "src/engine/SCons/compat/_scons_hashlib.py 2013/03/03 09:48:35 garyo" + +import md5 +from string import hexdigits + +class md5obj(object): + + md5_module = md5 + + def __init__(self, name, string=''): + if not name in ('MD5', 'md5'): + raise ValueError("unsupported hash type") + self.name = 'md5' + self.m = self.md5_module.md5() + + def __repr__(self): + return '<%s HASH object @ %#x>' % (self.name, id(self)) + + def copy(self): + import copy + result = copy.copy(self) + result.m = self.m.copy() + return result + + def digest(self): + return self.m.digest() + + def update(self, arg): + return self.m.update(arg) + + def hexdigest(self): + return self.m.hexdigest() + +new = md5obj + +def md5(string=''): + return md5obj('md5', string) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/compat/_scons_io.py b/scons/scons-local-2.3.0/SCons/compat/_scons_io.py new file mode 100644 index 000000000..194029efd --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/compat/_scons_io.py @@ -0,0 +1,45 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__doc__ = """ +io compatibility module for older (pre-2.6) Python versions + +This does not not NOT (repeat, *NOT*) provide complete io +functionality. It only wraps the portions of io functionality used +by SCons, in an interface that looks enough like io for our purposes. +""" + +__revision__ = "src/engine/SCons/compat/_scons_io.py 2013/03/03 09:48:35 garyo" + +# Use the "imp" module to protect the imports below from fixers. +import imp + +_cStringIO = imp.load_module('cStringIO', *imp.find_module('cStringIO')) +StringIO = _cStringIO.StringIO +del _cStringIO + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/compat/_scons_sets.py b/scons/scons-local-2.3.0/SCons/compat/_scons_sets.py new file mode 100644 index 000000000..0fde9941d --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/compat/_scons_sets.py @@ -0,0 +1,563 @@ +"""Classes to represent arbitrary sets (including sets of sets). + +This module implements sets using dictionaries whose values are +ignored. The usual operations (union, intersection, deletion, etc.) +are provided as both methods and operators. + +Important: sets are not sequences! While they support 'x in s', +'len(s)', and 'for x in s', none of those operations are unique for +sequences; for example, mappings support all three as well. The +characteristic operation for sequences is subscripting with small +integers: s[i], for i in range(len(s)). Sets don't support +subscripting at all. Also, sequences allow multiple occurrences and +their elements have a definite order; sets on the other hand don't +record multiple occurrences and don't remember the order of element +insertion (which is why they don't support s[i]). + +The following classes are provided: + +BaseSet -- All the operations common to both mutable and immutable + sets. This is an abstract class, not meant to be directly + instantiated. + +Set -- Mutable sets, subclass of BaseSet; not hashable. + +ImmutableSet -- Immutable sets, subclass of BaseSet; hashable. + An iterable argument is mandatory to create an ImmutableSet. + +_TemporarilyImmutableSet -- A wrapper around a Set, hashable, + giving the same hash value as the immutable set equivalent + would have. Do not use this class directly. + +Only hashable objects can be added to a Set. In particular, you cannot +really add a Set as an element to another Set; if you try, what is +actually added is an ImmutableSet built from it (it compares equal to +the one you tried adding). + +When you ask if `x in y' where x is a Set and y is a Set or +ImmutableSet, x is wrapped into a _TemporarilyImmutableSet z, and +what's tested is actually `z in y'. + +""" + +# Code history: +# +# - Greg V. Wilson wrote the first version, using a different approach +# to the mutable/immutable problem, and inheriting from dict. +# +# - Alex Martelli modified Greg's version to implement the current +# Set/ImmutableSet approach, and make the data an attribute. +# +# - Guido van Rossum rewrote much of the code, made some API changes, +# and cleaned up the docstrings. +# +# - Raymond Hettinger added a number of speedups and other +# improvements. + +# protect this import from the fixers... +exec('from itertools import ifilterfalse as filterfalse') + +__all__ = ['BaseSet', 'Set', 'ImmutableSet'] + +class BaseSet(object): + """Common base class for mutable and immutable sets.""" + + __slots__ = ['_data'] + + # Constructor + + def __init__(self): + """This is an abstract class.""" + # Don't call this from a concrete subclass! + if self.__class__ is BaseSet: + raise TypeError("BaseSet is an abstract class. " + "Use Set or ImmutableSet.") + + # Standard protocols: __len__, __repr__, __str__, __iter__ + + def __len__(self): + """Return the number of elements of a set.""" + return len(self._data) + + def __repr__(self): + """Return string representation of a set. + + This looks like 'Set([])'. + """ + return self._repr() + + # __str__ is the same as __repr__ + __str__ = __repr__ + + def _repr(self, sort_them=False): + elements = list(self._data.keys()) + if sort_them: + elements.sort() + return '%s(%r)' % (self.__class__.__name__, elements) + + def __iter__(self): + """Return an iterator over the elements or a set. + + This is the keys iterator for the underlying dict. + """ + # Wrapping name in () prevents fixer from "fixing" this + return (self._data.iterkeys)() + + # Three-way comparison is not supported. However, because __eq__ is + # tried before __cmp__, if Set x == Set y, x.__eq__(y) returns True and + # then cmp(x, y) returns 0 (Python doesn't actually call __cmp__ in this + # case). + + def __cmp__(self, other): + raise TypeError("can't compare sets using cmp()") + + # Equality comparisons using the underlying dicts. Mixed-type comparisons + # are allowed here, where Set == z for non-Set z always returns False, + # and Set != z always True. This allows expressions like "x in y" to + # give the expected result when y is a sequence of mixed types, not + # raising a pointless TypeError just because y contains a Set, or x is + # a Set and y contain's a non-set ("in" invokes only __eq__). + # Subtle: it would be nicer if __eq__ and __ne__ could return + # NotImplemented instead of True or False. Then the other comparand + # would get a chance to determine the result, and if the other comparand + # also returned NotImplemented then it would fall back to object address + # comparison (which would always return False for __eq__ and always + # True for __ne__). However, that doesn't work, because this type + # *also* implements __cmp__: if, e.g., __eq__ returns NotImplemented, + # Python tries __cmp__ next, and the __cmp__ here then raises TypeError. + + def __eq__(self, other): + if isinstance(other, BaseSet): + return self._data == other._data + else: + return False + + def __ne__(self, other): + if isinstance(other, BaseSet): + return self._data != other._data + else: + return True + + # Copying operations + + def copy(self): + """Return a shallow copy of a set.""" + result = self.__class__() + result._data.update(self._data) + return result + + __copy__ = copy # For the copy module + + def __deepcopy__(self, memo): + """Return a deep copy of a set; used by copy module.""" + # This pre-creates the result and inserts it in the memo + # early, in case the deep copy recurses into another reference + # to this same set. A set can't be an element of itself, but + # it can certainly contain an object that has a reference to + # itself. + from copy import deepcopy + result = self.__class__() + memo[id(self)] = result + data = result._data + value = True + for elt in self: + data[deepcopy(elt, memo)] = value + return result + + # Standard set operations: union, intersection, both differences. + # Each has an operator version (e.g. __or__, invoked with |) and a + # method version (e.g. union). + # Subtle: Each pair requires distinct code so that the outcome is + # correct when the type of other isn't suitable. For example, if + # we did "union = __or__" instead, then Set().union(3) would return + # NotImplemented instead of raising TypeError (albeit that *why* it + # raises TypeError as-is is also a bit subtle). + + def __or__(self, other): + """Return the union of two sets as a new set. + + (I.e. all elements that are in either set.) + """ + if not isinstance(other, BaseSet): + return NotImplemented + return self.union(other) + + def union(self, other): + """Return the union of two sets as a new set. + + (I.e. all elements that are in either set.) + """ + result = self.__class__(self) + result._update(other) + return result + + def __and__(self, other): + """Return the intersection of two sets as a new set. + + (I.e. all elements that are in both sets.) + """ + if not isinstance(other, BaseSet): + return NotImplemented + return self.intersection(other) + + def intersection(self, other): + """Return the intersection of two sets as a new set. + + (I.e. all elements that are in both sets.) + """ + if not isinstance(other, BaseSet): + other = Set(other) + if len(self) <= len(other): + little, big = self, other + else: + little, big = other, self + common = iter(filter(big._data.has_key, little)) + return self.__class__(common) + + def __xor__(self, other): + """Return the symmetric difference of two sets as a new set. + + (I.e. all elements that are in exactly one of the sets.) + """ + if not isinstance(other, BaseSet): + return NotImplemented + return self.symmetric_difference(other) + + def symmetric_difference(self, other): + """Return the symmetric difference of two sets as a new set. + + (I.e. all elements that are in exactly one of the sets.) + """ + result = self.__class__() + data = result._data + value = True + selfdata = self._data + try: + otherdata = other._data + except AttributeError: + otherdata = Set(other)._data + for elt in filterfalse(otherdata.has_key, selfdata): + data[elt] = value + for elt in filterfalse(selfdata.has_key, otherdata): + data[elt] = value + return result + + def __sub__(self, other): + """Return the difference of two sets as a new Set. + + (I.e. all elements that are in this set and not in the other.) + """ + if not isinstance(other, BaseSet): + return NotImplemented + return self.difference(other) + + def difference(self, other): + """Return the difference of two sets as a new Set. + + (I.e. all elements that are in this set and not in the other.) + """ + result = self.__class__() + data = result._data + try: + otherdata = other._data + except AttributeError: + otherdata = Set(other)._data + value = True + for elt in filterfalse(otherdata.has_key, self): + data[elt] = value + return result + + # Membership test + + def __contains__(self, element): + """Report whether an element is a member of a set. + + (Called in response to the expression `element in self'.) + """ + try: + return element in self._data + except TypeError: + transform = getattr(element, "__as_temporarily_immutable__", None) + if transform is None: + raise # re-raise the TypeError exception we caught + return transform() in self._data + + # Subset and superset test + + def issubset(self, other): + """Report whether another set contains this set.""" + self._binary_sanity_check(other) + if len(self) > len(other): # Fast check for obvious cases + return False + for elt in filterfalse(other._data.has_key, self): + return False + return True + + def issuperset(self, other): + """Report whether this set contains another set.""" + self._binary_sanity_check(other) + if len(self) < len(other): # Fast check for obvious cases + return False + for elt in filterfalse(self._data.has_key, other): + return False + return True + + # Inequality comparisons using the is-subset relation. + __le__ = issubset + __ge__ = issuperset + + def __lt__(self, other): + self._binary_sanity_check(other) + return len(self) < len(other) and self.issubset(other) + + def __gt__(self, other): + self._binary_sanity_check(other) + return len(self) > len(other) and self.issuperset(other) + + # Assorted helpers + + def _binary_sanity_check(self, other): + # Check that the other argument to a binary operation is also + # a set, raising a TypeError otherwise. + if not isinstance(other, BaseSet): + raise TypeError("Binary operation only permitted between sets") + + def _compute_hash(self): + # Calculate hash code for a set by xor'ing the hash codes of + # the elements. This ensures that the hash code does not depend + # on the order in which elements are added to the set. This is + # not called __hash__ because a BaseSet should not be hashable; + # only an ImmutableSet is hashable. + result = 0 + for elt in self: + result ^= hash(elt) + return result + + def _update(self, iterable): + # The main loop for update() and the subclass __init__() methods. + data = self._data + + # Use the fast update() method when a dictionary is available. + if isinstance(iterable, BaseSet): + data.update(iterable._data) + return + + value = True + + if type(iterable) in (list, tuple, xrange): + # Optimized: we know that __iter__() and next() can't + # raise TypeError, so we can move 'try:' out of the loop. + it = iter(iterable) + while True: + try: + for element in it: + data[element] = value + return + except TypeError: + transform = getattr(element, "__as_immutable__", None) + if transform is None: + raise # re-raise the TypeError exception we caught + data[transform()] = value + else: + # Safe: only catch TypeError where intended + for element in iterable: + try: + data[element] = value + except TypeError: + transform = getattr(element, "__as_immutable__", None) + if transform is None: + raise # re-raise the TypeError exception we caught + data[transform()] = value + + +class ImmutableSet(BaseSet): + """Immutable set class.""" + + __slots__ = ['_hashcode'] + + # BaseSet + hashing + + def __init__(self, iterable=None): + """Construct an immutable set from an optional iterable.""" + self._hashcode = None + self._data = {} + if iterable is not None: + self._update(iterable) + + def __hash__(self): + if self._hashcode is None: + self._hashcode = self._compute_hash() + return self._hashcode + + def __getstate__(self): + return self._data, self._hashcode + + def __setstate__(self, state): + self._data, self._hashcode = state + +class Set(BaseSet): + """ Mutable set class.""" + + __slots__ = [] + + # BaseSet + operations requiring mutability; no hashing + + def __init__(self, iterable=None): + """Construct a set from an optional iterable.""" + self._data = {} + if iterable is not None: + self._update(iterable) + + def __getstate__(self): + # getstate's results are ignored if it is not + return self._data, + + def __setstate__(self, data): + self._data, = data + + def __hash__(self): + """A Set cannot be hashed.""" + # We inherit object.__hash__, so we must deny this explicitly + raise TypeError("Can't hash a Set, only an ImmutableSet.") + + # In-place union, intersection, differences. + # Subtle: The xyz_update() functions deliberately return None, + # as do all mutating operations on built-in container types. + # The __xyz__ spellings have to return self, though. + + def __ior__(self, other): + """Update a set with the union of itself and another.""" + self._binary_sanity_check(other) + self._data.update(other._data) + return self + + def union_update(self, other): + """Update a set with the union of itself and another.""" + self._update(other) + + def __iand__(self, other): + """Update a set with the intersection of itself and another.""" + self._binary_sanity_check(other) + self._data = (self & other)._data + return self + + def intersection_update(self, other): + """Update a set with the intersection of itself and another.""" + if isinstance(other, BaseSet): + self &= other + else: + self._data = (self.intersection(other))._data + + def __ixor__(self, other): + """Update a set with the symmetric difference of itself and another.""" + self._binary_sanity_check(other) + self.symmetric_difference_update(other) + return self + + def symmetric_difference_update(self, other): + """Update a set with the symmetric difference of itself and another.""" + data = self._data + value = True + if not isinstance(other, BaseSet): + other = Set(other) + if self is other: + self.clear() + for elt in other: + if elt in data: + del data[elt] + else: + data[elt] = value + + def __isub__(self, other): + """Remove all elements of another set from this set.""" + self._binary_sanity_check(other) + self.difference_update(other) + return self + + def difference_update(self, other): + """Remove all elements of another set from this set.""" + data = self._data + if not isinstance(other, BaseSet): + other = Set(other) + if self is other: + self.clear() + for elt in filter(data.has_key, other): + del data[elt] + + # Python dict-like mass mutations: update, clear + + def update(self, iterable): + """Add all values from an iterable (such as a list or file).""" + self._update(iterable) + + def clear(self): + """Remove all elements from this set.""" + self._data.clear() + + # Single-element mutations: add, remove, discard + + def add(self, element): + """Add an element to a set. + + This has no effect if the element is already present. + """ + try: + self._data[element] = True + except TypeError: + transform = getattr(element, "__as_immutable__", None) + if transform is None: + raise # re-raise the TypeError exception we caught + self._data[transform()] = True + + def remove(self, element): + """Remove an element from a set; it must be a member. + + If the element is not a member, raise a KeyError. + """ + try: + del self._data[element] + except TypeError: + transform = getattr(element, "__as_temporarily_immutable__", None) + if transform is None: + raise # re-raise the TypeError exception we caught + del self._data[transform()] + + def discard(self, element): + """Remove an element from a set if it is a member. + + If the element is not a member, do nothing. + """ + try: + self.remove(element) + except KeyError: + pass + + def pop(self): + """Remove and return an arbitrary set element.""" + return self._data.popitem()[0] + + def __as_immutable__(self): + # Return a copy of self as an immutable set + return ImmutableSet(self) + + def __as_temporarily_immutable__(self): + # Return self wrapped in a temporarily immutable set + return _TemporarilyImmutableSet(self) + + +class _TemporarilyImmutableSet(BaseSet): + # Wrap a mutable set as if it was temporarily immutable. + # This only supplies hashing and equality comparisons. + + def __init__(self, set): + self._set = set + self._data = set._data # Needed by ImmutableSet.__eq__() + + def __hash__(self): + return self._set._compute_hash() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/compat/_scons_subprocess.py b/scons/scons-local-2.3.0/SCons/compat/_scons_subprocess.py new file mode 100644 index 000000000..eebe53d34 --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/compat/_scons_subprocess.py @@ -0,0 +1,1281 @@ +# subprocess - Subprocesses with accessible I/O streams +# +# For more information about this module, see PEP 324. +# +# This module should remain compatible with Python 2.2, see PEP 291. +# +# Copyright (c) 2003-2005 by Peter Astrand +# +# Licensed to PSF under a Contributor Agreement. +# See http://www.python.org/2.4/license for licensing details. + +r"""subprocess - Subprocesses with accessible I/O streams + +This module allows you to spawn processes, connect to their +input/output/error pipes, and obtain their return codes. This module +intends to replace several other, older modules and functions, like: + +os.system +os.spawn* +os.popen* +popen2.* +commands.* + +Information about how the subprocess module can be used to replace these +modules and functions can be found below. + + + +Using the subprocess module +=========================== +This module defines one class called Popen: + +class Popen(args, bufsize=0, executable=None, + stdin=None, stdout=None, stderr=None, + preexec_fn=None, close_fds=False, shell=False, + cwd=None, env=None, universal_newlines=False, + startupinfo=None, creationflags=0): + + +Arguments are: + +args should be a string, or a sequence of program arguments. The +program to execute is normally the first item in the args sequence or +string, but can be explicitly set by using the executable argument. + +On UNIX, with shell=False (default): In this case, the Popen class +uses os.execvp() to execute the child program. args should normally +be a sequence. A string will be treated as a sequence with the string +as the only item (the program to execute). + +On UNIX, with shell=True: If args is a string, it specifies the +command string to execute through the shell. If args is a sequence, +the first item specifies the command string, and any additional items +will be treated as additional shell arguments. + +On Windows: the Popen class uses CreateProcess() to execute the child +program, which operates on strings. If args is a sequence, it will be +converted to a string using the list2cmdline method. Please note that +not all MS Windows applications interpret the command line the same +way: The list2cmdline is designed for applications using the same +rules as the MS C runtime. + +bufsize, if given, has the same meaning as the corresponding argument +to the built-in open() function: 0 means unbuffered, 1 means line +buffered, any other positive value means use a buffer of +(approximately) that size. A negative bufsize means to use the system +default, which usually means fully buffered. The default value for +bufsize is 0 (unbuffered). + +stdin, stdout and stderr specify the executed programs' standard +input, standard output and standard error file handles, respectively. +Valid values are PIPE, an existing file descriptor (a positive +integer), an existing file object, and None. PIPE indicates that a +new pipe to the child should be created. With None, no redirection +will occur; the child's file handles will be inherited from the +parent. Additionally, stderr can be STDOUT, which indicates that the +stderr data from the applications should be captured into the same +file handle as for stdout. + +If preexec_fn is set to a callable object, this object will be called +in the child process just before the child is executed. + +If close_fds is true, all file descriptors except 0, 1 and 2 will be +closed before the child process is executed. + +if shell is true, the specified command will be executed through the +shell. + +If cwd is not None, the current directory will be changed to cwd +before the child is executed. + +If env is not None, it defines the environment variables for the new +process. + +If universal_newlines is true, the file objects stdout and stderr are +opened as a text files, but lines may be terminated by any of '\n', +the Unix end-of-line convention, '\r', the Macintosh convention or +'\r\n', the Windows convention. All of these external representations +are seen as '\n' by the Python program. Note: This feature is only +available if Python is built with universal newline support (the +default). Also, the newlines attribute of the file objects stdout, +stdin and stderr are not updated by the communicate() method. + +The startupinfo and creationflags, if given, will be passed to the +underlying CreateProcess() function. They can specify things such as +appearance of the main window and priority for the new process. +(Windows only) + + +This module also defines two shortcut functions: + +call(*popenargs, **kwargs): + Run command with arguments. Wait for command to complete, then + return the returncode attribute. + + The arguments are the same as for the Popen constructor. Example: + + retcode = call(["ls", "-l"]) + +check_call(*popenargs, **kwargs): + Run command with arguments. Wait for command to complete. If the + exit code was zero then return, otherwise raise + CalledProcessError. The CalledProcessError object will have the + return code in the returncode attribute. + + The arguments are the same as for the Popen constructor. Example: + + check_call(["ls", "-l"]) + +Exceptions +---------- +Exceptions raised in the child process, before the new program has +started to execute, will be re-raised in the parent. Additionally, +the exception object will have one extra attribute called +'child_traceback', which is a string containing traceback information +from the childs point of view. + +The most common exception raised is OSError. This occurs, for +example, when trying to execute a non-existent file. Applications +should prepare for OSErrors. + +A ValueError will be raised if Popen is called with invalid arguments. + +check_call() will raise CalledProcessError, if the called process +returns a non-zero return code. + + +Security +-------- +Unlike some other popen functions, this implementation will never call +/bin/sh implicitly. This means that all characters, including shell +metacharacters, can safely be passed to child processes. + + +Popen objects +============= +Instances of the Popen class have the following methods: + +poll() + Check if child process has terminated. Returns returncode + attribute. + +wait() + Wait for child process to terminate. Returns returncode attribute. + +communicate(input=None) + Interact with process: Send data to stdin. Read data from stdout + and stderr, until end-of-file is reached. Wait for process to + terminate. The optional stdin argument should be a string to be + sent to the child process, or None, if no data should be sent to + the child. + + communicate() returns a tuple (stdout, stderr). + + Note: The data read is buffered in memory, so do not use this + method if the data size is large or unlimited. + +The following attributes are also available: + +stdin + If the stdin argument is PIPE, this attribute is a file object + that provides input to the child process. Otherwise, it is None. + +stdout + If the stdout argument is PIPE, this attribute is a file object + that provides output from the child process. Otherwise, it is + None. + +stderr + If the stderr argument is PIPE, this attribute is file object that + provides error output from the child process. Otherwise, it is + None. + +pid + The process ID of the child process. + +returncode + The child return code. A None value indicates that the process + hasn't terminated yet. A negative value -N indicates that the + child was terminated by signal N (UNIX only). + + +Replacing older functions with the subprocess module +==================================================== +In this section, "a ==> b" means that b can be used as a replacement +for a. + +Note: All functions in this section fail (more or less) silently if +the executed program cannot be found; this module raises an OSError +exception. + +In the following examples, we assume that the subprocess module is +imported with "from subprocess import *". + + +Replacing /bin/sh shell backquote +--------------------------------- +output=`mycmd myarg` +==> +output = Popen(["mycmd", "myarg"], stdout=PIPE).communicate()[0] + + +Replacing shell pipe line +------------------------- +output=`dmesg | grep hda` +==> +p1 = Popen(["dmesg"], stdout=PIPE) +p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) +output = p2.communicate()[0] + + +Replacing os.system() +--------------------- +sts = os.system("mycmd" + " myarg") +==> +p = Popen("mycmd" + " myarg", shell=True) +pid, sts = os.waitpid(p.pid, 0) + +Note: + +* Calling the program through the shell is usually not required. + +* It's easier to look at the returncode attribute than the + exitstatus. + +A more real-world example would look like this: + +try: + retcode = call("mycmd" + " myarg", shell=True) + if retcode < 0: + print >>sys.stderr, "Child was terminated by signal", -retcode + else: + print >>sys.stderr, "Child returned", retcode +except OSError, e: + print >>sys.stderr, "Execution failed:", e + + +Replacing os.spawn* +------------------- +P_NOWAIT example: + +pid = os.spawnlp(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg") +==> +pid = Popen(["/bin/mycmd", "myarg"]).pid + + +P_WAIT example: + +retcode = os.spawnlp(os.P_WAIT, "/bin/mycmd", "mycmd", "myarg") +==> +retcode = call(["/bin/mycmd", "myarg"]) + + +Vector example: + +os.spawnvp(os.P_NOWAIT, path, args) +==> +Popen([path] + args[1:]) + + +Environment example: + +os.spawnlpe(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg", env) +==> +Popen(["/bin/mycmd", "myarg"], env={"PATH": "/usr/bin"}) + + +Replacing os.popen* +------------------- +pipe = os.popen(cmd, mode='r', bufsize) +==> +pipe = Popen(cmd, shell=True, bufsize=bufsize, stdout=PIPE).stdout + +pipe = os.popen(cmd, mode='w', bufsize) +==> +pipe = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE).stdin + + +(child_stdin, child_stdout) = os.popen2(cmd, mode, bufsize) +==> +p = Popen(cmd, shell=True, bufsize=bufsize, + stdin=PIPE, stdout=PIPE, close_fds=True) +(child_stdin, child_stdout) = (p.stdin, p.stdout) + + +(child_stdin, + child_stdout, + child_stderr) = os.popen3(cmd, mode, bufsize) +==> +p = Popen(cmd, shell=True, bufsize=bufsize, + stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) +(child_stdin, + child_stdout, + child_stderr) = (p.stdin, p.stdout, p.stderr) + + +(child_stdin, child_stdout_and_stderr) = os.popen4(cmd, mode, bufsize) +==> +p = Popen(cmd, shell=True, bufsize=bufsize, + stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) +(child_stdin, child_stdout_and_stderr) = (p.stdin, p.stdout) + + +Replacing popen2.* +------------------ +Note: If the cmd argument to popen2 functions is a string, the command +is executed through /bin/sh. If it is a list, the command is directly +executed. + +(child_stdout, child_stdin) = popen2.popen2("somestring", bufsize, mode) +==> +p = Popen(["somestring"], shell=True, bufsize=bufsize + stdin=PIPE, stdout=PIPE, close_fds=True) +(child_stdout, child_stdin) = (p.stdout, p.stdin) + + +(child_stdout, child_stdin) = popen2.popen2(["mycmd", "myarg"], bufsize, mode) +==> +p = Popen(["mycmd", "myarg"], bufsize=bufsize, + stdin=PIPE, stdout=PIPE, close_fds=True) +(child_stdout, child_stdin) = (p.stdout, p.stdin) + +The popen2.Popen3 and popen3.Popen4 basically works as subprocess.Popen, +except that: + +* subprocess.Popen raises an exception if the execution fails +* the capturestderr argument is replaced with the stderr argument. +* stdin=PIPE and stdout=PIPE must be specified. +* popen2 closes all filedescriptors by default, but you have to specify + close_fds=True with subprocess.Popen. + + +""" + +import sys +mswindows = (sys.platform == "win32") + +import os +import types +import traceback + +# Exception classes used by this module. +class CalledProcessError(Exception): + """This exception is raised when a process run by check_call() returns + a non-zero exit status. The exit status will be stored in the + returncode attribute.""" + def __init__(self, returncode, cmd): + self.returncode = returncode + self.cmd = cmd + def __str__(self): + return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode) + + +if mswindows: + try: + import threading + except ImportError: + # SCons: the threading module is only used by the communicate() + # method, which we don't actually use, so don't worry if we + # can't import it. + pass + import msvcrt + try: + # Try to get _subprocess + from _subprocess import * + class STARTUPINFO(object): + dwFlags = 0 + hStdInput = None + hStdOutput = None + hStdError = None + wShowWindow = 0 + class pywintypes(object): + error = IOError + except ImportError: + # If not there, then drop back to requiring pywin32 + # TODO: Should this be wrapped in try as well? To notify user to install + # pywin32 ? With URL to it? + import pywintypes + from win32api import GetStdHandle, STD_INPUT_HANDLE, \ + STD_OUTPUT_HANDLE, STD_ERROR_HANDLE + from win32api import GetCurrentProcess, DuplicateHandle, \ + GetModuleFileName, GetVersion + from win32con import DUPLICATE_SAME_ACCESS, SW_HIDE + from win32pipe import CreatePipe + from win32process import CreateProcess, STARTUPINFO, \ + GetExitCodeProcess, STARTF_USESTDHANDLES, \ + STARTF_USESHOWWINDOW, CREATE_NEW_CONSOLE + from win32event import WaitForSingleObject, INFINITE, WAIT_OBJECT_0 + + +else: + import select + import errno + import fcntl + import pickle + + try: + fcntl.F_GETFD + except AttributeError: + fcntl.F_GETFD = 1 + + try: + fcntl.F_SETFD + except AttributeError: + fcntl.F_SETFD = 2 + +__all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "CalledProcessError"] + +try: + MAXFD = os.sysconf("SC_OPEN_MAX") +except KeyboardInterrupt: + raise # SCons: don't swallow keyboard interrupts +except: + MAXFD = 256 + +try: + isinstance(1, int) +except TypeError: + def is_int(obj): + return isinstance(obj, type(1)) + def is_int_or_long(obj): + return type(obj) in (type(1), type(1L)) +else: + def is_int(obj): + return isinstance(obj, int) + def is_int_or_long(obj): + return isinstance(obj, (int, long)) + +try: + types.StringTypes +except AttributeError: + try: + types.StringTypes = (str, unicode) + except NameError: + types.StringTypes = (str,) +def is_string(obj): + return isinstance(obj, types.StringTypes) + +_active = [] + +def _cleanup(): + for inst in _active[:]: + if inst.poll(_deadstate=sys.maxsize) >= 0: + try: + _active.remove(inst) + except ValueError: + # This can happen if two threads create a new Popen instance. + # It's harmless that it was already removed, so ignore. + pass + +PIPE = -1 +STDOUT = -2 + + +def call(*popenargs, **kwargs): + """Run command with arguments. Wait for command to complete, then + return the returncode attribute. + + The arguments are the same as for the Popen constructor. Example: + + retcode = call(["ls", "-l"]) + """ + return apply(Popen, popenargs, kwargs).wait() + + +def check_call(*popenargs, **kwargs): + """Run command with arguments. Wait for command to complete. If + the exit code was zero then return, otherwise raise + CalledProcessError. The CalledProcessError object will have the + return code in the returncode attribute. + + The arguments are the same as for the Popen constructor. Example: + + check_call(["ls", "-l"]) + """ + retcode = call(*popenargs, **kwargs) + cmd = kwargs.get("args") + if cmd is None: + cmd = popenargs[0] + if retcode: + raise CalledProcessError(retcode, cmd) + return retcode + + +def list2cmdline(seq): + """ + Translate a sequence of arguments into a command line + string, using the same rules as the MS C runtime: + + 1) Arguments are delimited by white space, which is either a + space or a tab. + + 2) A string surrounded by double quotation marks is + interpreted as a single argument, regardless of white space + contained within. A quoted string can be embedded in an + argument. + + 3) A double quotation mark preceded by a backslash is + interpreted as a literal double quotation mark. + + 4) Backslashes are interpreted literally, unless they + immediately precede a double quotation mark. + + 5) If backslashes immediately precede a double quotation mark, + every pair of backslashes is interpreted as a literal + backslash. If the number of backslashes is odd, the last + backslash escapes the next double quotation mark as + described in rule 3. + """ + + # See + # http://msdn.microsoft.com/library/en-us/vccelng/htm/progs_12.asp + result = [] + needquote = False + for arg in seq: + bs_buf = [] + + # Add a space to separate this argument from the others + if result: + result.append(' ') + + needquote = (" " in arg) or ("\t" in arg) + if needquote: + result.append('"') + + for c in arg: + if c == '\\': + # Don't know if we need to double yet. + bs_buf.append(c) + elif c == '"': + # Double backspaces. + result.append('\\' * len(bs_buf)*2) + bs_buf = [] + result.append('\\"') + else: + # Normal char + if bs_buf: + result.extend(bs_buf) + bs_buf = [] + result.append(c) + + # Add remaining backspaces, if any. + if bs_buf: + result.extend(bs_buf) + + if needquote: + result.extend(bs_buf) + result.append('"') + + return ''.join(result) + +class Popen(object): + def __init__(self, args, bufsize=0, executable=None, + stdin=None, stdout=None, stderr=None, + preexec_fn=None, close_fds=False, shell=False, + cwd=None, env=None, universal_newlines=False, + startupinfo=None, creationflags=0): + """Create new Popen instance.""" + _cleanup() + + self._child_created = False + if not is_int_or_long(bufsize): + raise TypeError("bufsize must be an integer") + + if mswindows: + if preexec_fn is not None: + raise ValueError("preexec_fn is not supported on Windows " + "platforms") + if close_fds: + raise ValueError("close_fds is not supported on Windows " + "platforms") + else: + # POSIX + if startupinfo is not None: + raise ValueError("startupinfo is only supported on Windows " + "platforms") + if creationflags != 0: + raise ValueError("creationflags is only supported on Windows " + "platforms") + + self.stdin = None + self.stdout = None + self.stderr = None + self.pid = None + self.returncode = None + self.universal_newlines = universal_newlines + + # Input and output objects. The general principle is like + # this: + # + # Parent Child + # ------ ----- + # p2cwrite ---stdin---> p2cread + # c2pread <--stdout--- c2pwrite + # errread <--stderr--- errwrite + # + # On POSIX, the child objects are file descriptors. On + # Windows, these are Windows file handles. The parent objects + # are file descriptors on both platforms. The parent objects + # are None when not using PIPEs. The child objects are None + # when not redirecting. + + (p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite) = self._get_handles(stdin, stdout, stderr) + + self._execute_child(args, executable, preexec_fn, close_fds, + cwd, env, universal_newlines, + startupinfo, creationflags, shell, + p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite) + + if p2cwrite: + self.stdin = os.fdopen(p2cwrite, 'wb', bufsize) + if c2pread: + if universal_newlines: + self.stdout = os.fdopen(c2pread, 'rU', bufsize) + else: + self.stdout = os.fdopen(c2pread, 'rb', bufsize) + if errread: + if universal_newlines: + self.stderr = os.fdopen(errread, 'rU', bufsize) + else: + self.stderr = os.fdopen(errread, 'rb', bufsize) + + + def _translate_newlines(self, data): + data = data.replace("\r\n", "\n") + data = data.replace("\r", "\n") + return data + + + def __del__(self): + if not self._child_created: + # We didn't get to successfully create a child process. + return + # In case the child hasn't been waited on, check if it's done. + self.poll(_deadstate=sys.maxsize) + if self.returncode is None and _active is not None: + # Child is still running, keep us alive until we can wait on it. + _active.append(self) + + + def communicate(self, input=None): + """Interact with process: Send data to stdin. Read data from + stdout and stderr, until end-of-file is reached. Wait for + process to terminate. The optional input argument should be a + string to be sent to the child process, or None, if no data + should be sent to the child. + + communicate() returns a tuple (stdout, stderr).""" + + # Optimization: If we are only using one pipe, or no pipe at + # all, using select() or threads is unnecessary. + if [self.stdin, self.stdout, self.stderr].count(None) >= 2: + stdout = None + stderr = None + if self.stdin: + if input: + self.stdin.write(input) + self.stdin.close() + elif self.stdout: + stdout = self.stdout.read() + elif self.stderr: + stderr = self.stderr.read() + self.wait() + return (stdout, stderr) + + return self._communicate(input) + + + if mswindows: + # + # Windows methods + # + def _get_handles(self, stdin, stdout, stderr): + """Construct and return tupel with IO objects: + p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite + """ + if stdin is None and stdout is None and stderr is None: + return (None, None, None, None, None, None) + + p2cread, p2cwrite = None, None + c2pread, c2pwrite = None, None + errread, errwrite = None, None + + if stdin is None: + p2cread = GetStdHandle(STD_INPUT_HANDLE) + elif stdin == PIPE: + p2cread, p2cwrite = CreatePipe(None, 0) + # Detach and turn into fd + p2cwrite = p2cwrite.Detach() + p2cwrite = msvcrt.open_osfhandle(p2cwrite, 0) + elif is_int(stdin): + p2cread = msvcrt.get_osfhandle(stdin) + else: + # Assuming file-like object + p2cread = msvcrt.get_osfhandle(stdin.fileno()) + p2cread = self._make_inheritable(p2cread) + + if stdout is None: + c2pwrite = GetStdHandle(STD_OUTPUT_HANDLE) + elif stdout == PIPE: + c2pread, c2pwrite = CreatePipe(None, 0) + # Detach and turn into fd + c2pread = c2pread.Detach() + c2pread = msvcrt.open_osfhandle(c2pread, 0) + elif is_int(stdout): + c2pwrite = msvcrt.get_osfhandle(stdout) + else: + # Assuming file-like object + c2pwrite = msvcrt.get_osfhandle(stdout.fileno()) + c2pwrite = self._make_inheritable(c2pwrite) + + if stderr is None: + errwrite = GetStdHandle(STD_ERROR_HANDLE) + elif stderr == PIPE: + errread, errwrite = CreatePipe(None, 0) + # Detach and turn into fd + errread = errread.Detach() + errread = msvcrt.open_osfhandle(errread, 0) + elif stderr == STDOUT: + errwrite = c2pwrite + elif is_int(stderr): + errwrite = msvcrt.get_osfhandle(stderr) + else: + # Assuming file-like object + errwrite = msvcrt.get_osfhandle(stderr.fileno()) + errwrite = self._make_inheritable(errwrite) + + return (p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite) + + + def _make_inheritable(self, handle): + """Return a duplicate of handle, which is inheritable""" + return DuplicateHandle(GetCurrentProcess(), handle, + GetCurrentProcess(), 0, 1, + DUPLICATE_SAME_ACCESS) + + + def _find_w9xpopen(self): + """Find and return absolut path to w9xpopen.exe""" + w9xpopen = os.path.join(os.path.dirname(GetModuleFileName(0)), + "w9xpopen.exe") + if not os.path.exists(w9xpopen): + # Eeek - file-not-found - possibly an embedding + # situation - see if we can locate it in sys.exec_prefix + w9xpopen = os.path.join(os.path.dirname(sys.exec_prefix), + "w9xpopen.exe") + if not os.path.exists(w9xpopen): + raise RuntimeError("Cannot locate w9xpopen.exe, which is " + "needed for Popen to work with your " + "shell or platform.") + return w9xpopen + + + def _execute_child(self, args, executable, preexec_fn, close_fds, + cwd, env, universal_newlines, + startupinfo, creationflags, shell, + p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite): + """Execute program (MS Windows version)""" + + if not isinstance(args, types.StringTypes): + args = list2cmdline(args) + + # Process startup details + if startupinfo is None: + startupinfo = STARTUPINFO() + if None not in (p2cread, c2pwrite, errwrite): + startupinfo.dwFlags = startupinfo.dwFlags | STARTF_USESTDHANDLES + startupinfo.hStdInput = p2cread + startupinfo.hStdOutput = c2pwrite + startupinfo.hStdError = errwrite + + if shell: + startupinfo.dwFlags = startupinfo.dwFlags | STARTF_USESHOWWINDOW + startupinfo.wShowWindow = SW_HIDE + comspec = os.environ.get("COMSPEC", "cmd.exe") + args = comspec + " /c " + args + if (GetVersion() >= 0x80000000L or + os.path.basename(comspec).lower() == "command.com"): + # Win9x, or using command.com on NT. We need to + # use the w9xpopen intermediate program. For more + # information, see KB Q150956 + # (http://web.archive.org/web/20011105084002/http://support.microsoft.com/support/kb/articles/Q150/9/56.asp) + w9xpopen = self._find_w9xpopen() + args = '"%s" %s' % (w9xpopen, args) + # Not passing CREATE_NEW_CONSOLE has been known to + # cause random failures on win9x. Specifically a + # dialog: "Your program accessed mem currently in + # use at xxx" and a hopeful warning about the + # stability of your system. Cost is Ctrl+C wont + # kill children. + creationflags = creationflags | CREATE_NEW_CONSOLE + + # Start the process + try: + hp, ht, pid, tid = CreateProcess(executable, args, + # no special security + None, None, + # must inherit handles to pass std + # handles + 1, + creationflags, + env, + cwd, + startupinfo) + except pywintypes.error, e: + # Translate pywintypes.error to WindowsError, which is + # a subclass of OSError. FIXME: We should really + # translate errno using _sys_errlist (or simliar), but + # how can this be done from Python? + raise WindowsError(*e.args) + + # Retain the process handle, but close the thread handle + self._child_created = True + self._handle = hp + self.pid = pid + ht.Close() + + # Child is launched. Close the parent's copy of those pipe + # handles that only the child should have open. You need + # to make sure that no handles to the write end of the + # output pipe are maintained in this process or else the + # pipe will not close when the child process exits and the + # ReadFile will hang. + if p2cread is not None: + p2cread.Close() + if c2pwrite is not None: + c2pwrite.Close() + if errwrite is not None: + errwrite.Close() + + + def poll(self, _deadstate=None): + """Check if child process has terminated. Returns returncode + attribute.""" + if self.returncode is None: + if WaitForSingleObject(self._handle, 0) == WAIT_OBJECT_0: + self.returncode = GetExitCodeProcess(self._handle) + return self.returncode + + + def wait(self): + """Wait for child process to terminate. Returns returncode + attribute.""" + if self.returncode is None: + obj = WaitForSingleObject(self._handle, INFINITE) + self.returncode = GetExitCodeProcess(self._handle) + return self.returncode + + + def _readerthread(self, fh, buffer): + buffer.append(fh.read()) + + + def _communicate(self, input): + stdout = None # Return + stderr = None # Return + + if self.stdout: + stdout = [] + stdout_thread = threading.Thread(target=self._readerthread, + args=(self.stdout, stdout)) + stdout_thread.setDaemon(True) + stdout_thread.start() + if self.stderr: + stderr = [] + stderr_thread = threading.Thread(target=self._readerthread, + args=(self.stderr, stderr)) + stderr_thread.setDaemon(True) + stderr_thread.start() + + if self.stdin: + if input is not None: + self.stdin.write(input) + self.stdin.close() + + if self.stdout: + stdout_thread.join() + if self.stderr: + stderr_thread.join() + + # All data exchanged. Translate lists into strings. + if stdout is not None: + stdout = stdout[0] + if stderr is not None: + stderr = stderr[0] + + # Translate newlines, if requested. We cannot let the file + # object do the translation: It is based on stdio, which is + # impossible to combine with select (unless forcing no + # buffering). + if self.universal_newlines and hasattr(file, 'newlines'): + if stdout: + stdout = self._translate_newlines(stdout) + if stderr: + stderr = self._translate_newlines(stderr) + + self.wait() + return (stdout, stderr) + + else: + # + # POSIX methods + # + def _get_handles(self, stdin, stdout, stderr): + """Construct and return tupel with IO objects: + p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite + """ + p2cread, p2cwrite = None, None + c2pread, c2pwrite = None, None + errread, errwrite = None, None + + if stdin is None: + pass + elif stdin == PIPE: + p2cread, p2cwrite = os.pipe() + elif is_int(stdin): + p2cread = stdin + else: + # Assuming file-like object + p2cread = stdin.fileno() + + if stdout is None: + pass + elif stdout == PIPE: + c2pread, c2pwrite = os.pipe() + elif is_int(stdout): + c2pwrite = stdout + else: + # Assuming file-like object + c2pwrite = stdout.fileno() + + if stderr is None: + pass + elif stderr == PIPE: + errread, errwrite = os.pipe() + elif stderr == STDOUT: + errwrite = c2pwrite + elif is_int(stderr): + errwrite = stderr + else: + # Assuming file-like object + errwrite = stderr.fileno() + + return (p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite) + + + def _set_cloexec_flag(self, fd): + try: + cloexec_flag = fcntl.FD_CLOEXEC + except AttributeError: + cloexec_flag = 1 + + old = fcntl.fcntl(fd, fcntl.F_GETFD) + fcntl.fcntl(fd, fcntl.F_SETFD, old | cloexec_flag) + + + def _close_fds(self, but): + for i in range(3, MAXFD): + if i == but: + continue + try: + os.close(i) + except KeyboardInterrupt: + raise # SCons: don't swallow keyboard interrupts + except: + pass + + + def _execute_child(self, args, executable, preexec_fn, close_fds, + cwd, env, universal_newlines, + startupinfo, creationflags, shell, + p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite): + """Execute program (POSIX version)""" + + if is_string(args): + args = [args] + + if shell: + args = ["/bin/sh", "-c"] + args + + if executable is None: + executable = args[0] + + # For transferring possible exec failure from child to parent + # The first char specifies the exception type: 0 means + # OSError, 1 means some other error. + errpipe_read, errpipe_write = os.pipe() + self._set_cloexec_flag(errpipe_write) + + self.pid = os.fork() + self._child_created = True + if self.pid == 0: + # Child + try: + # Close parent's pipe ends + if p2cwrite: + os.close(p2cwrite) + if c2pread: + os.close(c2pread) + if errread: + os.close(errread) + os.close(errpipe_read) + + # Dup fds for child + if p2cread: + os.dup2(p2cread, 0) + if c2pwrite: + os.dup2(c2pwrite, 1) + if errwrite: + os.dup2(errwrite, 2) + + # Close pipe fds. Make sure we don't close the same + # fd more than once, or standard fds. + try: + set + except NameError: + # Fall-back for earlier Python versions, so epydoc + # can use this module directly to execute things. + if p2cread: + os.close(p2cread) + if c2pwrite and c2pwrite not in (p2cread,): + os.close(c2pwrite) + if errwrite and errwrite not in (p2cread, c2pwrite): + os.close(errwrite) + else: + for fd in set((p2cread, c2pwrite, errwrite))-set((0,1,2)): + if fd: os.close(fd) + + # Close all other fds, if asked for + if close_fds: + self._close_fds(but=errpipe_write) + + if cwd is not None: + os.chdir(cwd) + + if preexec_fn: + apply(preexec_fn) + + if env is None: + os.execvp(executable, args) + else: + os.execvpe(executable, args, env) + + except KeyboardInterrupt: + raise # SCons: don't swallow keyboard interrupts + + except: + exc_type, exc_value, tb = sys.exc_info() + # Save the traceback and attach it to the exception object + exc_lines = traceback.format_exception(exc_type, + exc_value, + tb) + exc_value.child_traceback = ''.join(exc_lines) + os.write(errpipe_write, pickle.dumps(exc_value)) + + # This exitcode won't be reported to applications, so it + # really doesn't matter what we return. + os._exit(255) + + # Parent + os.close(errpipe_write) + if p2cread and p2cwrite: + os.close(p2cread) + if c2pwrite and c2pread: + os.close(c2pwrite) + if errwrite and errread: + os.close(errwrite) + + # Wait for exec to fail or succeed; possibly raising exception + data = os.read(errpipe_read, 1048576) # Exceptions limited to 1 MB + os.close(errpipe_read) + if data != "": + os.waitpid(self.pid, 0) + child_exception = pickle.loads(data) + raise child_exception + + + def _handle_exitstatus(self, sts): + if os.WIFSIGNALED(sts): + self.returncode = -os.WTERMSIG(sts) + elif os.WIFEXITED(sts): + self.returncode = os.WEXITSTATUS(sts) + else: + # Should never happen + raise RuntimeError("Unknown child exit status!") + + + def poll(self, _deadstate=None): + """Check if child process has terminated. Returns returncode + attribute.""" + if self.returncode is None: + try: + pid, sts = os.waitpid(self.pid, os.WNOHANG) + if pid == self.pid: + self._handle_exitstatus(sts) + except os.error: + if _deadstate is not None: + self.returncode = _deadstate + return self.returncode + + + def wait(self): + """Wait for child process to terminate. Returns returncode + attribute.""" + if self.returncode is None: + pid, sts = os.waitpid(self.pid, 0) + self._handle_exitstatus(sts) + return self.returncode + + + def _communicate(self, input): + read_set = [] + write_set = [] + stdout = None # Return + stderr = None # Return + + if self.stdin: + # Flush stdio buffer. This might block, if the user has + # been writing to .stdin in an uncontrolled fashion. + self.stdin.flush() + if input: + write_set.append(self.stdin) + else: + self.stdin.close() + if self.stdout: + read_set.append(self.stdout) + stdout = [] + if self.stderr: + read_set.append(self.stderr) + stderr = [] + + input_offset = 0 + while read_set or write_set: + rlist, wlist, xlist = select.select(read_set, write_set, []) + + if self.stdin in wlist: + # When select has indicated that the file is writable, + # we can write up to PIPE_BUF bytes without risk + # blocking. POSIX defines PIPE_BUF >= 512 + m = memoryview(input)[input_offset:input_offset+512] + bytes_written = os.write(self.stdin.fileno(), m) + input_offset = input_offset + bytes_written + if input_offset >= len(input): + self.stdin.close() + write_set.remove(self.stdin) + + if self.stdout in rlist: + data = os.read(self.stdout.fileno(), 1024) + if data == "": + self.stdout.close() + read_set.remove(self.stdout) + stdout.append(data) + + if self.stderr in rlist: + data = os.read(self.stderr.fileno(), 1024) + if data == "": + self.stderr.close() + read_set.remove(self.stderr) + stderr.append(data) + + # All data exchanged. Translate lists into strings. + if stdout is not None: + stdout = ''.join(stdout) + if stderr is not None: + stderr = ''.join(stderr) + + # Translate newlines, if requested. We cannot let the file + # object do the translation: It is based on stdio, which is + # impossible to combine with select (unless forcing no + # buffering). + if self.universal_newlines and hasattr(file, 'newlines'): + if stdout: + stdout = self._translate_newlines(stdout) + if stderr: + stderr = self._translate_newlines(stderr) + + self.wait() + return (stdout, stderr) + + +def _demo_posix(): + # + # Example 1: Simple redirection: Get process list + # + plist = Popen(["ps"], stdout=PIPE).communicate()[0] + print "Process list:" + print plist + + # + # Example 2: Change uid before executing child + # + if os.getuid() == 0: + p = Popen(["id"], preexec_fn=lambda: os.setuid(100)) + p.wait() + + # + # Example 3: Connecting several subprocesses + # + print "Looking for 'hda'..." + p1 = Popen(["dmesg"], stdout=PIPE) + p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) + print repr(p2.communicate()[0]) + + # + # Example 4: Catch execution error + # + print + print "Trying a weird file..." + try: + print Popen(["/this/path/does/not/exist"]).communicate() + except OSError, e: + if e.errno == errno.ENOENT: + print "The file didn't exist. I thought so..." + print "Child traceback:" + print e.child_traceback + else: + print "Error", e.errno + else: + sys.stderr.write( "Gosh. No error.\n" ) + + +def _demo_windows(): + # + # Example 1: Connecting several subprocesses + # + print "Looking for 'PROMPT' in set output..." + p1 = Popen("set", stdout=PIPE, shell=True) + p2 = Popen('find "PROMPT"', stdin=p1.stdout, stdout=PIPE) + print repr(p2.communicate()[0]) + + # + # Example 2: Simple execution of program + # + print "Executing calc..." + p = Popen("calc") + p.wait() + + +if __name__ == "__main__": + if mswindows: + _demo_windows() + else: + _demo_posix() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/cpp.py b/scons/scons-local-2.3.0/SCons/cpp.py new file mode 100644 index 000000000..6999292dc --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/cpp.py @@ -0,0 +1,589 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/cpp.py 2013/03/03 09:48:35 garyo" + +__doc__ = """ +SCons C Pre-Processor module +""" +#TODO 2.3 and before has no sorted() +import SCons.compat + +import os +import re + +# +# First "subsystem" of regular expressions that we set up: +# +# Stuff to turn the C preprocessor directives in a file's contents into +# a list of tuples that we can process easily. +# + +# A table of regular expressions that fetch the arguments from the rest of +# a C preprocessor line. Different directives have different arguments +# that we want to fetch, using the regular expressions to which the lists +# of preprocessor directives map. +cpp_lines_dict = { + # Fetch the rest of a #if/#elif/#ifdef/#ifndef as one argument, + # separated from the keyword by white space. + ('if', 'elif', 'ifdef', 'ifndef',) + : '\s+(.+)', + + # Fetch the rest of a #import/#include/#include_next line as one + # argument, with white space optional. + ('import', 'include', 'include_next',) + : '\s*(.+)', + + # We don't care what comes after a #else or #endif line. + ('else', 'endif',) : '', + + # Fetch three arguments from a #define line: + # 1) The #defined keyword. + # 2) The optional parentheses and arguments (if it's a function-like + # macro, '' if it's not). + # 3) The expansion value. + ('define',) : '\s+([_A-Za-z][_A-Za-z0-9_]*)(\([^)]*\))?\s*(.*)', + + # Fetch the #undefed keyword from a #undef line. + ('undef',) : '\s+([_A-Za-z][A-Za-z0-9_]*)', +} + +# Create a table that maps each individual C preprocessor directive to +# the corresponding compiled regular expression that fetches the arguments +# we care about. +Table = {} +for op_list, expr in cpp_lines_dict.items(): + e = re.compile(expr) + for op in op_list: + Table[op] = e +del e +del op +del op_list + +# Create a list of the expressions we'll use to match all of the +# preprocessor directives. These are the same as the directives +# themselves *except* that we must use a negative lookahead assertion +# when matching "if" so it doesn't match the "if" in "ifdef." +override = { + 'if' : 'if(?!def)', +} +l = [override.get(x, x) for x in Table.keys()] + + +# Turn the list of expressions into one big honkin' regular expression +# that will match all the preprocessor lines at once. This will return +# a list of tuples, one for each preprocessor line. The preprocessor +# directive will be the first element in each tuple, and the rest of +# the line will be the second element. +e = '^\s*#\s*(' + '|'.join(l) + ')(.*)$' + +# And last but not least, compile the expression. +CPP_Expression = re.compile(e, re.M) + + + + +# +# Second "subsystem" of regular expressions that we set up: +# +# Stuff to translate a C preprocessor expression (as found on a #if or +# #elif line) into an equivalent Python expression that we can eval(). +# + +# A dictionary that maps the C representation of Boolean operators +# to their Python equivalents. +CPP_to_Python_Ops_Dict = { + '!' : ' not ', + '!=' : ' != ', + '&&' : ' and ', + '||' : ' or ', + '?' : ' and ', + ':' : ' or ', + '\r' : '', +} + +CPP_to_Python_Ops_Sub = lambda m: CPP_to_Python_Ops_Dict[m.group(0)] + +# We have to sort the keys by length so that longer expressions +# come *before* shorter expressions--in particular, "!=" must +# come before "!" in the alternation. Without this, the Python +# re module, as late as version 2.2.2, empirically matches the +# "!" in "!=" first, instead of finding the longest match. +# What's up with that? +l = sorted(CPP_to_Python_Ops_Dict.keys(), key=lambda a: len(a), reverse=True) + +# Turn the list of keys into one regular expression that will allow us +# to substitute all of the operators at once. +expr = '|'.join(map(re.escape, l)) + +# ...and compile the expression. +CPP_to_Python_Ops_Expression = re.compile(expr) + +# A separate list of expressions to be evaluated and substituted +# sequentially, not all at once. +CPP_to_Python_Eval_List = [ + ['defined\s+(\w+)', '"\\1" in __dict__'], + ['defined\s*\((\w+)\)', '"\\1" in __dict__'], + ['/\*.*\*/', ''], + ['/\*.*', ''], + ['//.*', ''], + ['(0x[0-9A-Fa-f]*)[UL]+', '\\1'], +] + +# Replace the string representations of the regular expressions in the +# list with compiled versions. +for l in CPP_to_Python_Eval_List: + l[0] = re.compile(l[0]) + +# Wrap up all of the above into a handy function. +def CPP_to_Python(s): + """ + Converts a C pre-processor expression into an equivalent + Python expression that can be evaluated. + """ + s = CPP_to_Python_Ops_Expression.sub(CPP_to_Python_Ops_Sub, s) + for expr, repl in CPP_to_Python_Eval_List: + s = expr.sub(repl, s) + return s + + + +del expr +del l +del override + + + +class FunctionEvaluator(object): + """ + Handles delayed evaluation of a #define function call. + """ + def __init__(self, name, args, expansion): + """ + Squirrels away the arguments and expansion value of a #define + macro function for later evaluation when we must actually expand + a value that uses it. + """ + self.name = name + self.args = function_arg_separator.split(args) + try: + expansion = expansion.split('##') + except AttributeError: + pass + self.expansion = expansion + def __call__(self, *values): + """ + Evaluates the expansion of a #define macro function called + with the specified values. + """ + if len(self.args) != len(values): + raise ValueError("Incorrect number of arguments to `%s'" % self.name) + # Create a dictionary that maps the macro arguments to the + # corresponding values in this "call." We'll use this when we + # eval() the expansion so that arguments will get expanded to + # the right values. + locals = {} + for k, v in zip(self.args, values): + locals[k] = v + + parts = [] + for s in self.expansion: + if not s in self.args: + s = repr(s) + parts.append(s) + statement = ' + '.join(parts) + + return eval(statement, globals(), locals) + + + +# Find line continuations. +line_continuations = re.compile('\\\\\r?\n') + +# Search for a "function call" macro on an expansion. Returns the +# two-tuple of the "function" name itself, and a string containing the +# arguments within the call parentheses. +function_name = re.compile('(\S+)\(([^)]*)\)') + +# Split a string containing comma-separated function call arguments into +# the separate arguments. +function_arg_separator = re.compile(',\s*') + + + +class PreProcessor(object): + """ + The main workhorse class for handling C pre-processing. + """ + def __init__(self, current=os.curdir, cpppath=(), dict={}, all=0): + global Table + + cpppath = tuple(cpppath) + + self.searchpath = { + '"' : (current,) + cpppath, + '<' : cpppath + (current,), + } + + # Initialize our C preprocessor namespace for tracking the + # values of #defined keywords. We use this namespace to look + # for keywords on #ifdef/#ifndef lines, and to eval() the + # expressions on #if/#elif lines (after massaging them from C to + # Python). + self.cpp_namespace = dict.copy() + self.cpp_namespace['__dict__'] = self.cpp_namespace + + if all: + self.do_include = self.all_include + + # For efficiency, a dispatch table maps each C preprocessor + # directive (#if, #define, etc.) to the method that should be + # called when we see it. We accomodate state changes (#if, + # #ifdef, #ifndef) by pushing the current dispatch table on a + # stack and changing what method gets called for each relevant + # directive we might see next at this level (#else, #elif). + # #endif will simply pop the stack. + d = { + 'scons_current_file' : self.scons_current_file + } + for op in Table.keys(): + d[op] = getattr(self, 'do_' + op) + self.default_table = d + + # Controlling methods. + + def tupleize(self, contents): + """ + Turns the contents of a file into a list of easily-processed + tuples describing the CPP lines in the file. + + The first element of each tuple is the line's preprocessor + directive (#if, #include, #define, etc., minus the initial '#'). + The remaining elements are specific to the type of directive, as + pulled apart by the regular expression. + """ + global CPP_Expression, Table + contents = line_continuations.sub('', contents) + cpp_tuples = CPP_Expression.findall(contents) + return [(m[0],) + Table[m[0]].match(m[1]).groups() for m in cpp_tuples] + + def __call__(self, file): + """ + Pre-processes a file. + + This is the main public entry point. + """ + self.current_file = file + return self.process_contents(self.read_file(file), file) + + def process_contents(self, contents, fname=None): + """ + Pre-processes a file contents. + + This is the main internal entry point. + """ + self.stack = [] + self.dispatch_table = self.default_table.copy() + self.current_file = fname + self.tuples = self.tupleize(contents) + + self.initialize_result(fname) + while self.tuples: + t = self.tuples.pop(0) + # Uncomment to see the list of tuples being processed (e.g., + # to validate the CPP lines are being translated correctly). + #print t + self.dispatch_table[t[0]](t) + return self.finalize_result(fname) + + # Dispatch table stack manipulation methods. + + def save(self): + """ + Pushes the current dispatch table on the stack and re-initializes + the current dispatch table to the default. + """ + self.stack.append(self.dispatch_table) + self.dispatch_table = self.default_table.copy() + + def restore(self): + """ + Pops the previous dispatch table off the stack and makes it the + current one. + """ + try: self.dispatch_table = self.stack.pop() + except IndexError: pass + + # Utility methods. + + def do_nothing(self, t): + """ + Null method for when we explicitly want the action for a + specific preprocessor directive to do nothing. + """ + pass + + def scons_current_file(self, t): + self.current_file = t[1] + + def eval_expression(self, t): + """ + Evaluates a C preprocessor expression. + + This is done by converting it to a Python equivalent and + eval()ing it in the C preprocessor namespace we use to + track #define values. + """ + t = CPP_to_Python(' '.join(t[1:])) + try: return eval(t, self.cpp_namespace) + except (NameError, TypeError): return 0 + + def initialize_result(self, fname): + self.result = [fname] + + def finalize_result(self, fname): + return self.result[1:] + + def find_include_file(self, t): + """ + Finds the #include file for a given preprocessor tuple. + """ + fname = t[2] + for d in self.searchpath[t[1]]: + if d == os.curdir: + f = fname + else: + f = os.path.join(d, fname) + if os.path.isfile(f): + return f + return None + + def read_file(self, file): + return open(file).read() + + # Start and stop processing include lines. + + def start_handling_includes(self, t=None): + """ + Causes the PreProcessor object to start processing #import, + #include and #include_next lines. + + This method will be called when a #if, #ifdef, #ifndef or #elif + evaluates True, or when we reach the #else in a #if, #ifdef, + #ifndef or #elif block where a condition already evaluated + False. + + """ + d = self.dispatch_table + d['import'] = self.do_import + d['include'] = self.do_include + d['include_next'] = self.do_include + + def stop_handling_includes(self, t=None): + """ + Causes the PreProcessor object to stop processing #import, + #include and #include_next lines. + + This method will be called when a #if, #ifdef, #ifndef or #elif + evaluates False, or when we reach the #else in a #if, #ifdef, + #ifndef or #elif block where a condition already evaluated True. + """ + d = self.dispatch_table + d['import'] = self.do_nothing + d['include'] = self.do_nothing + d['include_next'] = self.do_nothing + + # Default methods for handling all of the preprocessor directives. + # (Note that what actually gets called for a given directive at any + # point in time is really controlled by the dispatch_table.) + + def _do_if_else_condition(self, condition): + """ + Common logic for evaluating the conditions on #if, #ifdef and + #ifndef lines. + """ + self.save() + d = self.dispatch_table + if condition: + self.start_handling_includes() + d['elif'] = self.stop_handling_includes + d['else'] = self.stop_handling_includes + else: + self.stop_handling_includes() + d['elif'] = self.do_elif + d['else'] = self.start_handling_includes + + def do_ifdef(self, t): + """ + Default handling of a #ifdef line. + """ + self._do_if_else_condition(t[1] in self.cpp_namespace) + + def do_ifndef(self, t): + """ + Default handling of a #ifndef line. + """ + self._do_if_else_condition(t[1] not in self.cpp_namespace) + + def do_if(self, t): + """ + Default handling of a #if line. + """ + self._do_if_else_condition(self.eval_expression(t)) + + def do_elif(self, t): + """ + Default handling of a #elif line. + """ + d = self.dispatch_table + if self.eval_expression(t): + self.start_handling_includes() + d['elif'] = self.stop_handling_includes + d['else'] = self.stop_handling_includes + + def do_else(self, t): + """ + Default handling of a #else line. + """ + pass + + def do_endif(self, t): + """ + Default handling of a #endif line. + """ + self.restore() + + def do_define(self, t): + """ + Default handling of a #define line. + """ + _, name, args, expansion = t + try: + expansion = int(expansion) + except (TypeError, ValueError): + pass + if args: + evaluator = FunctionEvaluator(name, args[1:-1], expansion) + self.cpp_namespace[name] = evaluator + else: + self.cpp_namespace[name] = expansion + + def do_undef(self, t): + """ + Default handling of a #undef line. + """ + try: del self.cpp_namespace[t[1]] + except KeyError: pass + + def do_import(self, t): + """ + Default handling of a #import line. + """ + # XXX finish this -- maybe borrow/share logic from do_include()...? + pass + + def do_include(self, t): + """ + Default handling of a #include line. + """ + t = self.resolve_include(t) + include_file = self.find_include_file(t) + if include_file: + #print "include_file =", include_file + self.result.append(include_file) + contents = self.read_file(include_file) + new_tuples = [('scons_current_file', include_file)] + \ + self.tupleize(contents) + \ + [('scons_current_file', self.current_file)] + self.tuples[:] = new_tuples + self.tuples + + # Date: Tue, 22 Nov 2005 20:26:09 -0500 + # From: Stefan Seefeld + # + # By the way, #include_next is not the same as #include. The difference + # being that #include_next starts its search in the path following the + # path that let to the including file. In other words, if your system + # include paths are ['/foo', '/bar'], and you are looking at a header + # '/foo/baz.h', it might issue an '#include_next ' which would + # correctly resolve to '/bar/baz.h' (if that exists), but *not* see + # '/foo/baz.h' again. See http://www.delorie.com/gnu/docs/gcc/cpp_11.html + # for more reasoning. + # + # I have no idea in what context 'import' might be used. + + # XXX is #include_next really the same as #include ? + do_include_next = do_include + + # Utility methods for handling resolution of include files. + + def resolve_include(self, t): + """Resolve a tuple-ized #include line. + + This handles recursive expansion of values without "" or <> + surrounding the name until an initial " or < is found, to handle + #include FILE + where FILE is a #define somewhere else. + """ + s = t[1] + while not s[0] in '<"': + #print "s =", s + try: + s = self.cpp_namespace[s] + except KeyError: + m = function_name.search(s) + s = self.cpp_namespace[m.group(1)] + if callable(s): + args = function_arg_separator.split(m.group(2)) + s = s(*args) + if not s: + return None + return (t[0], s[0], s[1:-1]) + + def all_include(self, t): + """ + """ + self.result.append(self.resolve_include(t)) + +class DumbPreProcessor(PreProcessor): + """A preprocessor that ignores all #if/#elif/#else/#endif directives + and just reports back *all* of the #include files (like the classic + SCons scanner did). + + This is functionally equivalent to using a regular expression to + find all of the #include lines, only slower. It exists mainly as + an example of how the main PreProcessor class can be sub-classed + to tailor its behavior. + """ + def __init__(self, *args, **kw): + PreProcessor.__init__(self, *args, **kw) + d = self.default_table + for func in ['if', 'elif', 'else', 'endif', 'ifdef', 'ifndef']: + d[func] = d[func] = self.do_nothing + +del __revision__ + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/dblite.py b/scons/scons-local-2.3.0/SCons/dblite.py new file mode 100644 index 000000000..f4ba90a1f --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/dblite.py @@ -0,0 +1,254 @@ +# dblite.py module contributed by Ralf W. Grosse-Kunstleve. +# Extended for Unicode by Steven Knight. + +import SCons.compat + +import builtins +import os +# compat layer imports "cPickle" for us if it's available. +import pickle +import shutil +import time + +keep_all_files = 00000 +ignore_corrupt_dbfiles = 0 + +def corruption_warning(filename): + print "Warning: Discarding corrupt database:", filename + +try: unicode +except NameError: + def is_string(s): + return isinstance(s, str) +else: + def is_string(s): + return type(s) in (str, unicode) + +try: + unicode('a') +except NameError: + def unicode(s): return s + +dblite_suffix = '.dblite' +tmp_suffix = '.tmp' + +class dblite(object): + + # Squirrel away references to the functions in various modules + # that we'll use when our __del__() method calls our sync() method + # during shutdown. We might get destroyed when Python is in the midst + # of tearing down the different modules we import in an essentially + # arbitrary order, and some of the various modules's global attributes + # may already be wiped out from under us. + # + # See the discussion at: + # http://mail.python.org/pipermail/python-bugs-list/2003-March/016877.html + + _open = builtins.open + _pickle_dump = staticmethod(pickle.dump) + _os_chmod = os.chmod + try: + _os_chown = os.chown + except AttributeError: + _os_chown = None + _os_rename = os.rename + _os_unlink = os.unlink + _shutil_copyfile = shutil.copyfile + _time_time = time.time + + def __init__(self, file_base_name, flag, mode): + assert flag in (None, "r", "w", "c", "n") + if (flag is None): flag = "r" + base, ext = os.path.splitext(file_base_name) + if ext == dblite_suffix: + # There's already a suffix on the file name, don't add one. + self._file_name = file_base_name + self._tmp_name = base + tmp_suffix + else: + self._file_name = file_base_name + dblite_suffix + self._tmp_name = file_base_name + tmp_suffix + self._flag = flag + self._mode = mode + self._dict = {} + self._needs_sync = 00000 + if self._os_chown is not None and (os.geteuid()==0 or os.getuid()==0): + # running as root; chown back to current owner/group when done + try: + statinfo = os.stat(self._file_name) + self._chown_to = statinfo.st_uid + self._chgrp_to = statinfo.st_gid + except OSError, e: + # db file doesn't exist yet. + # Check os.environ for SUDO_UID, use if set + self._chown_to = int(os.environ.get('SUDO_UID', -1)) + self._chgrp_to = int(os.environ.get('SUDO_GID', -1)) + else: + self._chown_to = -1 # don't chown + self._chgrp_to = -1 # don't chgrp + if (self._flag == "n"): + self._open(self._file_name, "wb", self._mode) + else: + try: + f = self._open(self._file_name, "rb") + except IOError, e: + if (self._flag != "c"): + raise e + self._open(self._file_name, "wb", self._mode) + else: + p = f.read() + if (len(p) > 0): + try: + self._dict = pickle.loads(p) + except (pickle.UnpicklingError, EOFError): + if (ignore_corrupt_dbfiles == 0): raise + if (ignore_corrupt_dbfiles == 1): + corruption_warning(self._file_name) + + def close(self): + if (self._needs_sync): + self.sync() + + def __del__(self): + self.close() + + def sync(self): + self._check_writable() + f = self._open(self._tmp_name, "wb", self._mode) + self._pickle_dump(self._dict, f, 1) + f.close() + # Windows doesn't allow renaming if the file exists, so unlink + # it first, chmod'ing it to make sure we can do so. On UNIX, we + # may not be able to chmod the file if it's owned by someone else + # (e.g. from a previous run as root). We should still be able to + # unlink() the file if the directory's writable, though, so ignore + # any OSError exception thrown by the chmod() call. + try: self._os_chmod(self._file_name, 0777) + except OSError: pass + self._os_unlink(self._file_name) + self._os_rename(self._tmp_name, self._file_name) + if self._os_chown is not None and self._chown_to > 0: # don't chown to root or -1 + try: + self._os_chown(self._file_name, self._chown_to, self._chgrp_to) + except OSError: + pass + self._needs_sync = 00000 + if (keep_all_files): + self._shutil_copyfile( + self._file_name, + self._file_name + "_" + str(int(self._time_time()))) + + def _check_writable(self): + if (self._flag == "r"): + raise IOError("Read-only database: %s" % self._file_name) + + def __getitem__(self, key): + return self._dict[key] + + def __setitem__(self, key, value): + self._check_writable() + if (not is_string(key)): + raise TypeError("key `%s' must be a string but is %s" % (key, type(key))) + if (not is_string(value)): + raise TypeError("value `%s' must be a string but is %s" % (value, type(value))) + self._dict[key] = value + self._needs_sync = 0001 + + def keys(self): + return list(self._dict.keys()) + + def has_key(self, key): + return key in self._dict + + def __contains__(self, key): + return key in self._dict + + def iterkeys(self): + # Wrapping name in () prevents fixer from "fixing" this + return (self._dict.iterkeys)() + + __iter__ = iterkeys + + def __len__(self): + return len(self._dict) + +def open(file, flag=None, mode=0666): + return dblite(file, flag, mode) + +def _exercise(): + db = open("tmp", "n") + assert len(db) == 0 + db["foo"] = "bar" + assert db["foo"] == "bar" + db[unicode("ufoo")] = unicode("ubar") + assert db[unicode("ufoo")] == unicode("ubar") + db.sync() + db = open("tmp", "c") + assert len(db) == 2, len(db) + assert db["foo"] == "bar" + db["bar"] = "foo" + assert db["bar"] == "foo" + db[unicode("ubar")] = unicode("ufoo") + assert db[unicode("ubar")] == unicode("ufoo") + db.sync() + db = open("tmp", "r") + assert len(db) == 4, len(db) + assert db["foo"] == "bar" + assert db["bar"] == "foo" + assert db[unicode("ufoo")] == unicode("ubar") + assert db[unicode("ubar")] == unicode("ufoo") + try: + db.sync() + except IOError, e: + assert str(e) == "Read-only database: tmp.dblite" + else: + raise RuntimeError("IOError expected.") + db = open("tmp", "w") + assert len(db) == 4 + db["ping"] = "pong" + db.sync() + try: + db[(1,2)] = "tuple" + except TypeError, e: + assert str(e) == "key `(1, 2)' must be a string but is ", str(e) + else: + raise RuntimeError("TypeError exception expected") + try: + db["list"] = [1,2] + except TypeError, e: + assert str(e) == "value `[1, 2]' must be a string but is ", str(e) + else: + raise RuntimeError("TypeError exception expected") + db = open("tmp", "r") + assert len(db) == 5 + db = open("tmp", "n") + assert len(db) == 0 + dblite._open("tmp.dblite", "w") + db = open("tmp", "r") + dblite._open("tmp.dblite", "w").write("x") + try: + db = open("tmp", "r") + except pickle.UnpicklingError: + pass + else: + raise RuntimeError("pickle exception expected.") + global ignore_corrupt_dbfiles + ignore_corrupt_dbfiles = 2 + db = open("tmp", "r") + assert len(db) == 0 + os.unlink("tmp.dblite") + try: + db = open("tmp", "w") + except IOError, e: + assert str(e) == "[Errno 2] No such file or directory: 'tmp.dblite'", str(e) + else: + raise RuntimeError("IOError expected.") + print "OK" + +if (__name__ == "__main__"): + _exercise() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/SCons/exitfuncs.py b/scons/scons-local-2.3.0/SCons/exitfuncs.py new file mode 100644 index 000000000..26df1983d --- /dev/null +++ b/scons/scons-local-2.3.0/SCons/exitfuncs.py @@ -0,0 +1,64 @@ +"""SCons.exitfuncs + +Register functions which are executed when SCons exits for any reason. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation +# +# 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. +# + +__revision__ = "src/engine/SCons/exitfuncs.py 2013/03/03 09:48:35 garyo" + + +import atexit + +_exithandlers = [] +def _run_exitfuncs(): + """run any registered exit functions + + _exithandlers is traversed in reverse order so functions are executed + last in, first out. + """ + + while _exithandlers: + func, targs, kargs = _exithandlers.pop() + func(*targs, **kargs) + +def register(func, *targs, **kargs): + """register a function to be executed upon normal program termination + + func - function to be called at exit + targs - optional arguments to pass to func + kargs - optional keyword arguments to pass to func + """ + _exithandlers.append((func, targs, kargs)) + + +# make our exit function get run by python when it exits +atexit.register(_run_exitfuncs) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/scons/scons-local-2.3.0/scons-2.3.0.egg-info b/scons/scons-local-2.3.0/scons-2.3.0.egg-info new file mode 100644 index 000000000..cd00aa720 --- /dev/null +++ b/scons/scons-local-2.3.0/scons-2.3.0.egg-info @@ -0,0 +1,13 @@ +Metadata-Version: 1.0 +Name: scons +Version: 2.3.0 +Summary: Open Source next-generation build tool. +Home-page: http://www.scons.org/ +Author: Steven Knight +Author-email: knight@baldmt.com +License: UNKNOWN +Description: Open Source next-generation build tool. + Improved, cross-platform substitute for the classic Make + utility. In short, SCons is an easier, more reliable + and faster way to build software. +Platform: UNKNOWN From 126d1b73d6c533c347534c96a267678aeee96c32 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 22 May 2013 06:45:35 -0700 Subject: [PATCH 070/110] reflect debug symbolizer in python --- SConstruct | 2 +- bindings/python/mapnik_debug_symbolizer.cpp | 42 +++++++++++++++++++++ include/mapnik/version.hpp | 2 +- 3 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 bindings/python/mapnik_debug_symbolizer.cpp diff --git a/SConstruct b/SConstruct index 0f96b0a89..488c36193 100644 --- a/SConstruct +++ b/SConstruct @@ -1574,7 +1574,7 @@ if not preconfigured: # fetch the mapnik version header in order to set the # ABI version used to build libmapnik.so on linux in src/build.py abi = conf.GetMapnikLibVersion() - abi_fallback = "2.2.0-pre" + abi_fallback = "2.2.0" if not abi: color_print(1,'Problem encountered parsing mapnik version, falling back to %s' % abi_fallback) abi = abi_fallback diff --git a/bindings/python/mapnik_debug_symbolizer.cpp b/bindings/python/mapnik_debug_symbolizer.cpp new file mode 100644 index 000000000..6b255967f --- /dev/null +++ b/bindings/python/mapnik_debug_symbolizer.cpp @@ -0,0 +1,42 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2013 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +#include +#include "mapnik_enumeration.hpp" +#include + +void export_debug_symbolizer() +{ + using namespace boost::python; + + mapnik::enumeration_("debug_symbolizer_mode") + .value("COLLISION",mapnik::DEBUG_SYM_MODE_COLLISION) + .value("VERTEX",mapnik::DEBUG_SYM_MODE_VERTEX) + ; + + class_("DebugSymbolizer", + init<>("Default debug Symbolizer")) + .add_property("mode", + &mapnik::debug_symbolizer::get_mode, + &mapnik::debug_symbolizer::set_mode) + ; +} diff --git a/include/mapnik/version.hpp b/include/mapnik/version.hpp index 35fd1b2a6..e05a3eaeb 100644 --- a/include/mapnik/version.hpp +++ b/include/mapnik/version.hpp @@ -23,7 +23,7 @@ #ifndef MAPNIK_VERSION_HPP #define MAPNIK_VERSION_HPP -#define MAPNIK_VERSION_IS_RELEASE 0 +#define MAPNIK_VERSION_IS_RELEASE 1 #define MAPNIK_MAJOR_VERSION 2 #define MAPNIK_MINOR_VERSION 2 From a3eaebd5e18d646b0f94eb65578b48c7cadd2eac Mon Sep 17 00:00:00 2001 From: artemp Date: Wed, 22 May 2013 11:07:55 +0100 Subject: [PATCH 071/110] feature_at_point + use 'tol' where possible --- plugins/input/ogr/ogr_datasource.cpp | 8 +++----- plugins/input/sqlite/sqlite_datasource.cpp | 7 +++---- src/memory_datasource.cpp | 3 +-- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/plugins/input/ogr/ogr_datasource.cpp b/plugins/input/ogr/ogr_datasource.cpp index 8385598f4..2e2f61758 100644 --- a/plugins/input/ogr/ogr_datasource.cpp +++ b/plugins/input/ogr/ogr_datasource.cpp @@ -535,13 +535,11 @@ featureset_ptr ogr_datasource::features_at_point(coord2d const& pt, double tol) } else { - OGRPoint point; - point.setX (pt.x); - point.setY (pt.y); - + mapnik::box2d bbox(pt, pt); + bbox.pad(tol); return featureset_ptr(new ogr_featureset (ctx, *layer, - point, + bbox, desc_.get_encoding())); } } diff --git a/plugins/input/sqlite/sqlite_datasource.cpp b/plugins/input/sqlite/sqlite_datasource.cpp index fa021ebf1..d5b5e9426 100644 --- a/plugins/input/sqlite/sqlite_datasource.cpp +++ b/plugins/input/sqlite/sqlite_datasource.cpp @@ -288,7 +288,7 @@ sqlite_datasource::sqlite_datasource(parameters const& params) if (boost::filesystem::exists(mapnik::utf8_to_utf16(index_db))) #else if (boost::filesystem::exists(index_db)) -#endif +#endif { dataset_->execute("attach database '" + index_db + "' as " + index_table_); } @@ -639,9 +639,8 @@ featureset_ptr sqlite_datasource::features_at_point(coord2d const& pt, double to if (dataset_) { - // TODO - need tolerance - mapnik::box2d const e(pt.x, pt.y, pt.x, pt.y); - + mapnik::box2d e(pt.x, pt.y, pt.x, pt.y); + e.pad(tol); std::ostringstream s; mapnik::context_ptr ctx = boost::make_shared(); diff --git a/src/memory_datasource.cpp b/src/memory_datasource.cpp index f198f3e74..46dbb151b 100644 --- a/src/memory_datasource.cpp +++ b/src/memory_datasource.cpp @@ -90,9 +90,8 @@ featureset_ptr memory_datasource::features(const query& q) const featureset_ptr memory_datasource::features_at_point(coord2d const& pt, double tol) const { box2d box = box2d(pt.x, pt.y, pt.x, pt.y); - + box.pad(tol); MAPNIK_LOG_DEBUG(memory_datasource) << "memory_datasource: Box=" << box << ", Point x=" << pt.x << ",y=" << pt.y; - return boost::make_shared(box,*this); } From 653e9e73b4851392ea95b2c2e11452130234565a Mon Sep 17 00:00:00 2001 From: artemp Date: Wed, 22 May 2013 16:38:25 +0100 Subject: [PATCH 072/110] + implement features_at_point in term of existing features(query) --- plugins/input/geojson/geojson_datasource.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/plugins/input/geojson/geojson_datasource.cpp b/plugins/input/geojson/geojson_datasource.cpp index 566cb5ee2..1ae885a8d 100644 --- a/plugins/input/geojson/geojson_datasource.cpp +++ b/plugins/input/geojson/geojson_datasource.cpp @@ -209,9 +209,17 @@ mapnik::featureset_ptr geojson_datasource::features(mapnik::query const& q) cons return mapnik::featureset_ptr(); } -// FIXME mapnik::featureset_ptr geojson_datasource::features_at_point(mapnik::coord2d const& pt, double tol) const { - throw mapnik::datasource_exception("GeoJSON Plugin: features_at_point is not supported yet"); - return mapnik::featureset_ptr(); + mapnik::box2d query_bbox(pt, pt); + query_bbox.pad(tol); + mapnik::query q(query_bbox); + std::vector const& desc = desc_.get_descriptors(); + std::vector::const_iterator itr = desc.begin(); + std::vector::const_iterator end = desc.end(); + for ( ;itr!=end;++itr) + { + q.add_property_name(itr->get_name()); + } + return features(q); } From 9b6e65e223c58bb6ee9856a0cf6186078e12d550 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Wed, 22 May 2013 18:00:22 +0200 Subject: [PATCH 073/110] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f3e83d1e..60c9cf2ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ Released --- (Packaged from ---) -Summary: The 2.2.0 release is the fastest running and most stable release in the history of Mapnik. The code line represents development in the master branch since the release of 2.1.0 in Aug 2012 and therefore includes nearly a year of bugfixes and low level optimizations. Shapefile and PostGIS datasources have benefited from numerous stability fixes, 64 bit integer support has been added to support OSM data, and many rendering fixes have landed for high quality output when using a rendering `scale_factor`. Many critical code paths have been optimized extensively include raster rendering, xml map loading, string to number conversion, vector reprojection when using `epsg:4326` and `epsg:3857`, `hextree` encoding, halo rendering, and rendering when using a custom `gamma`. Mapnik 2.2 also compiles faster than previous releases in the 2.x series and drops several uneeded and hard to install dependencies. +Summary: The 2.2.0 release is the fastest running and most stable release in the history of Mapnik. The code line represents development in the master branch since the release of 2.1.0 in Aug 2012 and therefore includes nearly a year of bugfixes and low level optimizations. Shapefile and PostGIS datasources have benefited from numerous stability fixes, 64 bit integer support has been added to support OSM data, and many rendering fixes have landed for high quality output when using a rendering `scale_factor`. Many critical code paths have been optimized extensively include raster rendering, xml map loading, string to number conversion, vector reprojection when using `epsg:4326` and `epsg:3857`, `hextree` encoding, halo rendering, and rendering when using a custom `gamma`. Mapnik 2.2 also compiles faster than previous releases in the 2.x series and drops several unneeded and hard to install dependencies. - Removed 3 depedencies without loosing any functionality: `ltdl`, `cairomm` and `libsigc++` (#1804,#806,#1681) From 40d262b2ddf660ac32dada733aa1491b3aaa6917 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Wed, 22 May 2013 18:03:40 +0200 Subject: [PATCH 074/110] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60c9cf2ef..a2becf50a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ Summary: The 2.2.0 release is the fastest running and most stable release in the - Added 64 bit integer support in expressions, feature ids, and the grid_renderer (#1661,#1662,#1662) -- Added the ability to disable needing various depedencies: `proj4`, `libpng`, `libtiff`, `libjpeg` +- Added the ability to disable the need for various dependencies: `proj4`, `libpng`, `libtiff`, `libjpeg` - Added faster reprojection support between `epsg:3857` and `epsg:4326` (#1705,#1703,#1579) From 95d5b73d86461110467923847525d61332a90edc Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 22 May 2013 09:13:05 -0700 Subject: [PATCH 075/110] fix reading inline csv data and reading from an xml string when building with ptree/rapidxml - closes #1857 and closes #1856 --- src/rapidxml_loader.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/rapidxml_loader.cpp b/src/rapidxml_loader.cpp index dcd172a0a..dda323118 100644 --- a/src/rapidxml_loader.cpp +++ b/src/rapidxml_loader.cpp @@ -79,8 +79,9 @@ public: try { // Parse using appropriate flags - const int f_tws = rapidxml::parse_normalize_whitespace - | rapidxml::parse_trim_whitespace; + // https://github.com/mapnik/mapnik/issues/1856 + // const int f_tws = rapidxml::parse_normalize_whitespace; + const int f_tws = rapidxml::parse_trim_whitespace; rapidxml::xml_document<> doc; doc.parse(&v.front()); @@ -110,8 +111,10 @@ public: // } // } - - load(buffer, node); + // https://github.com/mapnik/mapnik/issues/1857 + std::stringstream s; + s << buffer; + load(s, node); } private: void populate_tree(rapidxml::xml_node *cur_node, xml_node &node) @@ -141,10 +144,7 @@ private: case rapidxml::node_data: case rapidxml::node_cdata: { - std::string trimmed(cur_node->value()); - mapnik::util::trim(trimmed); - if (trimmed.empty()) break; //Don't add empty text nodes - node.add_child(trimmed, 0, true); + node.add_child(cur_node->value(), 0, true); } break; default: From 23878cf03ecc2dc83fe888e4dd05fab549835498 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 22 May 2013 09:21:56 -0700 Subject: [PATCH 076/110] fix #1858 --- SConstruct | 7 ++-- tests/cpp_tests/build.py | 75 ++++++++++++++++++++-------------------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/SConstruct b/SConstruct index 488c36193..f36603501 100644 --- a/SConstruct +++ b/SConstruct @@ -1867,11 +1867,10 @@ if not HELP_REQUESTED: SConscript('fonts/build.py') # build C++ tests - if env['CPP_TESTS']: - SConscript('tests/cpp_tests/build.py') + SConscript('tests/cpp_tests/build.py') - if env['SVG_RENDERER']: - SConscript('tests/cpp_tests/svg_renderer_tests/build.py') + if env['CPP_TESTS'] and env['SVG_RENDERER']: + SConscript('tests/cpp_tests/svg_renderer_tests/build.py') if env['BENCHMARK']: SConscript('benchmark/build.py') diff --git a/tests/cpp_tests/build.py b/tests/cpp_tests/build.py index d47c167bc..af79532bf 100644 --- a/tests/cpp_tests/build.py +++ b/tests/cpp_tests/build.py @@ -6,40 +6,41 @@ Import ('env') test_env = env.Clone() -test_env['LIBS'] = copy(env['LIBMAPNIK_LIBS']) -test_env.AppendUnique(LIBS='mapnik') -test_env.AppendUnique(LIBS='sqlite3') -test_env.AppendUnique(CXXFLAGS='-g') - -test_env['CXXFLAGS'] = copy(test_env['LIBMAPNIK_CXXFLAGS']) - -if test_env['HAS_CAIRO']: - test_env.PrependUnique(CPPPATH=test_env['CAIRO_CPPPATHS']) - test_env.Append(CPPDEFINES = '-DHAVE_CAIRO') - -for cpp_test in glob.glob('*_test.cpp'): - name = cpp_test.replace('.cpp','-bin') - source_files = [cpp_test] - test_program = None - # enable for faster compile while developing just this test - #if 'agg_blend_src_over_test' in cpp_test: - if False: - # customization here for faster compile - agg_env = Environment(ENV=os.environ) - agg_env['CXX'] = env['CXX'] - agg_env['CXXFLAGS'] = env['CXXFLAGS'] - if 'agg' in test_env['LIBS']: - agg_env.AppendUnique(LIBS='agg') - agg_env.Append(CPPPATH = '#deps/agg/include') - agg_env.Append(LIBPATH = '#deps/agg') - agg_env['CPPPATH'] = ['#deps/agg/include',env['BOOST_INCLUDES']] - test_program = agg_env.Program(name, source=source_files, LINKFLAGS=env['CUSTOM_LDFLAGS']) - else: - test_env_local = test_env.Clone() - if 'csv_parse' in cpp_test: - source_files += glob.glob('../../plugins/input/csv/' + '*.cpp') - test_program = test_env_local.Program(name, source=source_files, LINKFLAGS=env['CUSTOM_LDFLAGS']) - Depends(test_program, env.subst('../../src/%s' % env['MAPNIK_LIB_NAME'])) - # build locally if installing - if 'install' in COMMAND_LINE_TARGETS: - env.Alias('install',test_program) +if not env['CPP_TESTS']: + for cpp_test_bin in glob.glob('*-bin'): + os.unlink(cpp_test_bin) +else: + test_env['LIBS'] = copy(env['LIBMAPNIK_LIBS']) + test_env.AppendUnique(LIBS='mapnik') + test_env.AppendUnique(LIBS='sqlite3') + test_env.AppendUnique(CXXFLAGS='-g') + test_env['CXXFLAGS'] = copy(test_env['LIBMAPNIK_CXXFLAGS']) + if test_env['HAS_CAIRO']: + test_env.PrependUnique(CPPPATH=test_env['CAIRO_CPPPATHS']) + test_env.Append(CPPDEFINES = '-DHAVE_CAIRO') + for cpp_test in glob.glob('*_test.cpp'): + name = cpp_test.replace('.cpp','-bin') + source_files = [cpp_test] + test_program = None + # enable for faster compile while developing just this test + #if 'agg_blend_src_over_test' in cpp_test: + if False: + # customization here for faster compile + agg_env = Environment(ENV=os.environ) + agg_env['CXX'] = env['CXX'] + agg_env['CXXFLAGS'] = env['CXXFLAGS'] + if 'agg' in test_env['LIBS']: + agg_env.AppendUnique(LIBS='agg') + agg_env.Append(CPPPATH = '#deps/agg/include') + agg_env.Append(LIBPATH = '#deps/agg') + agg_env['CPPPATH'] = ['#deps/agg/include',env['BOOST_INCLUDES']] + test_program = agg_env.Program(name, source=source_files, LINKFLAGS=env['CUSTOM_LDFLAGS']) + else: + test_env_local = test_env.Clone() + if 'csv_parse' in cpp_test: + source_files += glob.glob('../../plugins/input/csv/' + '*.cpp') + test_program = test_env_local.Program(name, source=source_files, LINKFLAGS=env['CUSTOM_LDFLAGS']) + Depends(test_program, env.subst('../../src/%s' % env['MAPNIK_LIB_NAME'])) + # build locally if installing + if 'install' in COMMAND_LINE_TARGETS: + env.Alias('install',test_program) From 46b3bfb54712fa082b97c8862ef9f94a56b2d30d Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 22 May 2013 09:22:47 -0700 Subject: [PATCH 077/110] fix test behavior with postgis < 2.x --- tests/python_tests/postgis_test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/python_tests/postgis_test.py b/tests/python_tests/postgis_test.py index 8e7bda160..b9d91e278 100644 --- a/tests/python_tests/postgis_test.py +++ b/tests/python_tests/postgis_test.py @@ -497,7 +497,8 @@ if 'postgis' in mapnik.DatasourceCache.plugin_names() \ def test_that_64bit_int_fields_work(): ds = mapnik.PostGIS(dbname=MAPNIK_TEST_DBNAME, - table='test8') + table='test8', + geometry_field='geom') eq_(len(ds.fields()),2) eq_(ds.fields(),['gid','int_field']) eq_(ds.field_types(),['int','int']) From 31ae5e632b5243357ac5a3a46aa8aabaa205c8f4 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 22 May 2013 10:22:25 -0700 Subject: [PATCH 078/110] rollback accidentally commited change in 126d1b73d6c533 - not quite yet --- SConstruct | 2 +- include/mapnik/version.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/SConstruct b/SConstruct index f36603501..a25b03e0c 100644 --- a/SConstruct +++ b/SConstruct @@ -1574,7 +1574,7 @@ if not preconfigured: # fetch the mapnik version header in order to set the # ABI version used to build libmapnik.so on linux in src/build.py abi = conf.GetMapnikLibVersion() - abi_fallback = "2.2.0" + abi_fallback = "2.2.0-pre" if not abi: color_print(1,'Problem encountered parsing mapnik version, falling back to %s' % abi_fallback) abi = abi_fallback diff --git a/include/mapnik/version.hpp b/include/mapnik/version.hpp index e05a3eaeb..35fd1b2a6 100644 --- a/include/mapnik/version.hpp +++ b/include/mapnik/version.hpp @@ -23,7 +23,7 @@ #ifndef MAPNIK_VERSION_HPP #define MAPNIK_VERSION_HPP -#define MAPNIK_VERSION_IS_RELEASE 1 +#define MAPNIK_VERSION_IS_RELEASE 0 #define MAPNIK_MAJOR_VERSION 2 #define MAPNIK_MINOR_VERSION 2 From 9f4bde3078eaf3108fc428b76d775fe0352b22ec Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 22 May 2013 13:16:12 -0700 Subject: [PATCH 079/110] fix unsigned/signed comparision warning --- plugins/input/raster/raster_datasource.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/input/raster/raster_datasource.cpp b/plugins/input/raster/raster_datasource.cpp index 25541857d..01dda1592 100644 --- a/plugins/input/raster/raster_datasource.cpp +++ b/plugins/input/raster/raster_datasource.cpp @@ -191,7 +191,7 @@ featureset_ptr raster_datasource::features(query const& q) const return boost::make_shared >(policy, extent_, q); } - else if (width * height > (tile_size_ * tile_size_ << 2)) + else if (width * height > static_cast(tile_size_ * tile_size_ << 2)) { MAPNIK_LOG_DEBUG(raster) << "raster_datasource: Tiled policy"; From 031139f6fe48e8776c6f1fa4680ca2aee818a617 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 22 May 2013 13:48:56 -0700 Subject: [PATCH 080/110] disable building of python plugin by default - works around #1838 --- SConstruct | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SConstruct b/SConstruct index a25b03e0c..525e22bf8 100644 --- a/SConstruct +++ b/SConstruct @@ -109,7 +109,7 @@ PLUGINS = { # plugins with external dependencies 'csv': {'default':True,'path':None,'inc':None,'lib':None,'lang':'C++'}, 'raster': {'default':True,'path':None,'inc':None,'lib':None,'lang':'C++'}, 'geojson': {'default':True,'path':None,'inc':None,'lib':None,'lang':'C++'}, - 'python': {'default':True,'path':None,'inc':None,'lib':None,'lang':'C++'}, + 'python': {'default':False,'path':None,'inc':None,'lib':None,'lang':'C++'}, } From 584aad0c887e6c9a0f003bee719ed0395ede9267 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 22 May 2013 14:11:28 -0700 Subject: [PATCH 081/110] fix windows linking to clipper - needed by node-mapnik is_solid check --- deps/clipper/include/clipper.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/clipper/include/clipper.hpp b/deps/clipper/include/clipper.hpp index 78f6b1ca0..4683ef38d 100755 --- a/deps/clipper/include/clipper.hpp +++ b/deps/clipper/include/clipper.hpp @@ -105,7 +105,7 @@ private: enum JoinType { jtSquare, jtRound, jtMiter }; bool Orientation(const Polygon &poly); -double Area(const Polygon &poly); +MAPNIK_DECL double Area(const Polygon &poly); void OffsetPolygons(const Polygons &in_polys, Polygons &out_polys, double delta, JoinType jointype = jtSquare, double limit = 0, bool autoFix = true); From 1c296aa4d455a4e4122ab2fc26f634a00fd7aeef Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 22 May 2013 14:31:19 -0700 Subject: [PATCH 082/110] fix #1860 - refs #1826 --- deps/agg/include/agg_vpgen_clip_polygon.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/deps/agg/include/agg_vpgen_clip_polygon.h b/deps/agg/include/agg_vpgen_clip_polygon.h index ded754e21..c2f8cbd66 100644 --- a/deps/agg/include/agg_vpgen_clip_polygon.h +++ b/deps/agg/include/agg_vpgen_clip_polygon.h @@ -18,6 +18,11 @@ #include "agg_basics.h" +// https://github.com/mapnik/mapnik/issues/1860 +#ifdef __GNUC__ +#include +#endif + namespace agg { @@ -25,7 +30,8 @@ namespace agg // // See Implementation agg_vpgen_clip_polygon.cpp // - class vpgen_clip_polygon + + class MAPNIK_DECL vpgen_clip_polygon { public: vpgen_clip_polygon() : From 4ba061f506a51d33f02e4807cc5e2292bd8b85ae Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 22 May 2013 15:10:19 -0700 Subject: [PATCH 083/110] higher threshold for difference between cairo visual tests --- tests/visual_tests/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/visual_tests/test.py b/tests/visual_tests/test.py index d83ec943d..6755905aa 100755 --- a/tests/visual_tests/test.py +++ b/tests/visual_tests/test.py @@ -243,7 +243,7 @@ renderers = [ { 'name': 'cairo', 'render': render_cairo, 'compare': lambda actual, reference: compare(actual, reference, alpha=False), - 'threshold': 1, + 'threshold': 10, 'filetype': 'png', 'dir': 'images' }, From dfec39224b2c374790e88bb70fefb36ec693bf83 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 22 May 2013 15:42:28 -0700 Subject: [PATCH 084/110] renable cairo visual tests if cairo is installed --- tests/visual_tests/test.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/visual_tests/test.py b/tests/visual_tests/test.py index 6755905aa..905555cdc 100755 --- a/tests/visual_tests/test.py +++ b/tests/visual_tests/test.py @@ -20,9 +20,7 @@ defaults = { 'sizes': [(500, 100)], 'scales':[1.0,2.0], 'agg': True, - 'cairo': True, - # disabled for 2.2.x since cairo tests are unstable (springmeyer) - #'cairo': mapnik.has_cairo(), + 'cairo': mapnik.has_cairo(), 'grid': True } From 5aecb3c5f72daee6684f6dcbb1aeec10bee2eef7 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 22 May 2013 16:19:10 -0700 Subject: [PATCH 085/110] disable building osm plugin by default now that it is no longer needed for visual tests - refs #1822 --- SConstruct | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SConstruct b/SConstruct index 525e22bf8..65df4116d 100644 --- a/SConstruct +++ b/SConstruct @@ -102,7 +102,7 @@ PLUGINS = { # plugins with external dependencies 'rasterlite': {'default':False,'path':'RASTERLITE','inc':['sqlite3.h','rasterlite.h'],'lib':'rasterlite','lang':'C'}, # todo: osm plugin does also depend on libxml2 (but there is a separate check for that) - 'osm': {'default':True,'path':None,'inc':'curl/curl.h','lib':'curl','lang':'C'}, + 'osm': {'default':False,'path':None,'inc':'curl/curl.h','lib':'curl','lang':'C'}, # plugins without external dependencies requiring CheckLibWithHeader... 'shape': {'default':True,'path':None,'inc':None,'lib':None,'lang':'C++'}, From 5083a24f8915bc0506050a107e3cb5b72092e0c7 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 22 May 2013 17:03:08 -0700 Subject: [PATCH 086/110] postgis test: ensure threaded test fails early --- tests/python_tests/postgis_test.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/python_tests/postgis_test.py b/tests/python_tests/postgis_test.py index b9d91e278..e9f0d1ca4 100644 --- a/tests/python_tests/postgis_test.py +++ b/tests/python_tests/postgis_test.py @@ -476,6 +476,10 @@ if 'postgis' in mapnik.DatasourceCache.plugin_names() \ fs = ds.all_features() def test_threaded_create(NUM_THREADS=100): + # run one to start before thread loop + # to ensure that a throw stops the test + # from running all threads + create_ds() for i in range(NUM_THREADS): t = threading.Thread(target=create_ds) t.start() From 427308d6546fc78ab1414acc478794e12f3674af Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 22 May 2013 17:20:17 -0700 Subject: [PATCH 087/110] postgis test: fix for postgis < 2.x --- tests/python_tests/postgis_test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/python_tests/postgis_test.py b/tests/python_tests/postgis_test.py index e9f0d1ca4..4c3c75f4e 100644 --- a/tests/python_tests/postgis_test.py +++ b/tests/python_tests/postgis_test.py @@ -472,7 +472,8 @@ if 'postgis' in mapnik.DatasourceCache.plugin_names() \ def create_ds(): ds = mapnik.PostGIS(dbname=MAPNIK_TEST_DBNAME, table='test', - max_size=20) + max_size=20, + geometry_field='geom') fs = ds.all_features() def test_threaded_create(NUM_THREADS=100): From 6155d90dd13c915ac9cd437222e8a94e13387513 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 22 May 2013 17:54:32 -0700 Subject: [PATCH 088/110] better clearing of non requested or non buildable plugins --- SConstruct | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/SConstruct b/SConstruct index 65df4116d..85eca3171 100644 --- a/SConstruct +++ b/SConstruct @@ -1796,27 +1796,29 @@ if not HELP_REQUESTED: # Build the requested and able-to-be-compiled input plug-ins GDAL_BUILT = False OGR_BUILT = False - for plugin in env['REQUESTED_PLUGINS']: - details = env['PLUGINS'][plugin] - if details['lib'] in env['LIBS']: - if env['PLUGIN_LINKING'] == 'shared': - SConscript('plugins/input/%s/build.py' % plugin) - if plugin == 'ogr': OGR_BUILT = True - if plugin == 'gdal': GDAL_BUILT = True - if plugin == 'ogr' or plugin == 'gdal': - if GDAL_BUILT and OGR_BUILT: + for plugin in env['PLUGINS']: + if plugin in env['REQUESTED_PLUGINS']: + details = env['PLUGINS'][plugin] + if details['lib'] in env['LIBS']: + if env['PLUGIN_LINKING'] == 'shared': + SConscript('plugins/input/%s/build.py' % plugin) + if plugin == 'ogr': OGR_BUILT = True + if plugin == 'gdal': GDAL_BUILT = True + if plugin == 'ogr' or plugin == 'gdal': + if GDAL_BUILT and OGR_BUILT: + env['LIBS'].remove(details['lib']) + else: env['LIBS'].remove(details['lib']) + elif not details['lib']: + if env['PLUGIN_LINKING'] == 'shared': + # build internal datasource input plugins + SConscript('plugins/input/%s/build.py' % plugin) else: - env['LIBS'].remove(details['lib']) - elif not details['lib']: - if env['PLUGIN_LINKING'] == 'shared': - # build internal datasource input plugins - SConscript('plugins/input/%s/build.py' % plugin) - else: - color_print(1,"Notice: dependencies not met for plugin '%s', not building..." % plugin) - # also clear out locally built target - if os.path.exists('plugins/input/%s.input' % plugin): - os.unlink('plugins/input/%s.input' % plugin) + color_print(1,"Notice: dependencies not met for plugin '%s', not building..." % plugin) + if os.path.exists('plugins/input/%s.input' % plugin): + os.unlink('plugins/input/%s.input' % plugin) + elif os.path.exists('plugins/input/%s.input' % plugin): + os.unlink('plugins/input/%s.input' % plugin) create_uninstall_target(env, env['MAPNIK_LIB_DIR_DEST'], False) create_uninstall_target(env, env['MAPNIK_INPUT_PLUGINS_DEST'] , False) @@ -1829,7 +1831,7 @@ if not HELP_REQUESTED: if os.path.exists(plugin_path): if plugin not in env['REQUESTED_PLUGINS'] or env['PLUGIN_LINKING'] == 'static': color_print(3,"Notice: removing out of date plugin: '%s'" % plugin_path) - os.unlink(plugin_path) + os.unlink(plugin_path) # Build the c++ rundemo app if requested if env['DEMO']: From 00035b92d7dc29f50c7edba10db7f99c81937a04 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 22 May 2013 21:40:14 -0700 Subject: [PATCH 089/110] add static version of mapnik-config.bat for windows --- utils/mapnik-config/mapnik-config.bat | 50 +++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 utils/mapnik-config/mapnik-config.bat diff --git a/utils/mapnik-config/mapnik-config.bat b/utils/mapnik-config/mapnik-config.bat new file mode 100644 index 000000000..af26c09c6 --- /dev/null +++ b/utils/mapnik-config/mapnik-config.bat @@ -0,0 +1,50 @@ +@echo off + +set MAPNIK_VERSION=v2.2.0 +set MAPNIK_PREFIX=c:\\mapnik-%MAPNIK_VERSION% +set MAPNIK_LIBS=%MAPNIK_PREFIX%\lib +set MAPNIK_INCLUDES=%MAPNIK_PREFIX%\include +set MAPNIK_INPUT_PLUGINS_DIRECTORY=%MAPNIK_PREFIX%\input +set MAPNIK_FONTS_DIRECTORY=%MAPNIK_PREFIX%\fonts + +if /i "%1"=="" goto help +if /i "%1"=="help" goto help +if /i "%1"=="--help" goto help +if /i "%1"=="-help" goto help +if /i "%1"=="/help" goto help +if /i "%1"=="?" goto help +if /i "%1"=="-?" goto help +if /i "%1"=="--?" goto help +if /i "%1"=="/?" goto help +if /i "%1"=="--prefix" echo %MAPNIK_PREFIX% +if /i "%1"=="--libs" echo mapnik.lib +@rem TODO - figure out how to avoid hardcoding these library names +if /i "%1"=="--dep-libs" echo icuuc.lib icuin.lib libboost_system-vc100-mt-s-1_49.lib +if /i "%1"=="--ldflags" echo %MAPNIK_LIBS% +if /i "%1"=="--defines" echo _WINDOWS HAVE_JPEG HAVE_PNG HAVE_TIFF MAPNIK_USE_PROJ4 BOOST_REGEX_HAS_ICU MAPNIK_THREADSAFE BIGINT HAVE_LIBXML2 HAVE_CAIRO +@rem /MD is multithreaded dynamic linking - http://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx +@rem /EHsc is to support c++ exceptions - http://msdn.microsoft.com/en-us/library/1deeycx5(v=vs.80).aspx +@rem /GR is to support rtti (runtime type detection) - http://msdn.microsoft.com/en-us/library/we6hfdy0.aspx +if /i "%1"=="--cxxflags" echo /MD /EHsc /GR +if /i "%1"=="--includes" echo %MAPNIK_INCLUDES% +if /i "%1"=="--input-plugins" echo %MAPNIK_INPUT_PLUGINS_DIRECTORY% +if /i "%1"=="--fonts" echo %MAPNIK_FONTS_DIRECTORY% +goto exit + + +:help +echo mapnik-config.bat +echo Examples: +echo --libs : provide lib name for mapnik.dll +echo --defines : provide compiler defines needed for this mapnik build +echo --dep-libs : provide lib names of depedencies +echo --ldflags : provide lib paths to depedencies +echo --cxxflags : provide compiler flags +echo --includes : provide header paths for mapnik +echo --dep-includes : provide header paths for dependencies +echo --input-plugins : provide path to input plugins directory +echo --fonts : provide path to fonts directory +goto exit + +:exit +goto :EOF From 36123141ff2c92891bbdc6e3055b072262982de0 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 22 May 2013 22:06:36 -0700 Subject: [PATCH 090/110] fix windows build --- deps/agg/include/agg_vpgen_clip_polygon.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/deps/agg/include/agg_vpgen_clip_polygon.h b/deps/agg/include/agg_vpgen_clip_polygon.h index c2f8cbd66..4972a390b 100644 --- a/deps/agg/include/agg_vpgen_clip_polygon.h +++ b/deps/agg/include/agg_vpgen_clip_polygon.h @@ -19,9 +19,7 @@ #include "agg_basics.h" // https://github.com/mapnik/mapnik/issues/1860 -#ifdef __GNUC__ #include -#endif namespace agg { From fa7e4e24942a94828813e73264d20727806fc037 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 22 May 2013 22:07:00 -0700 Subject: [PATCH 091/110] excape path separators to be more gyp friendly --- utils/mapnik-config/mapnik-config.bat | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/utils/mapnik-config/mapnik-config.bat b/utils/mapnik-config/mapnik-config.bat index af26c09c6..bd11dd4ca 100644 --- a/utils/mapnik-config/mapnik-config.bat +++ b/utils/mapnik-config/mapnik-config.bat @@ -2,10 +2,10 @@ set MAPNIK_VERSION=v2.2.0 set MAPNIK_PREFIX=c:\\mapnik-%MAPNIK_VERSION% -set MAPNIK_LIBS=%MAPNIK_PREFIX%\lib -set MAPNIK_INCLUDES=%MAPNIK_PREFIX%\include -set MAPNIK_INPUT_PLUGINS_DIRECTORY=%MAPNIK_PREFIX%\input -set MAPNIK_FONTS_DIRECTORY=%MAPNIK_PREFIX%\fonts +set MAPNIK_LIBS=%MAPNIK_PREFIX%\\lib +set MAPNIK_INCLUDES=%MAPNIK_PREFIX%\\include +set MAPNIK_INPUT_PLUGINS_DIRECTORY=%MAPNIK_PREFIX%\\input +set MAPNIK_FONTS_DIRECTORY=%MAPNIK_PREFIX%\\fonts if /i "%1"=="" goto help if /i "%1"=="help" goto help From a20adb86be36401d71be0cc6318b39f000c2241b Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 22 May 2013 22:12:09 -0700 Subject: [PATCH 092/110] add agg includes directory --- utils/mapnik-config/mapnik-config.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/mapnik-config/mapnik-config.bat b/utils/mapnik-config/mapnik-config.bat index bd11dd4ca..929e60e45 100644 --- a/utils/mapnik-config/mapnik-config.bat +++ b/utils/mapnik-config/mapnik-config.bat @@ -26,7 +26,7 @@ if /i "%1"=="--defines" echo _WINDOWS HAVE_JPEG HAVE_PNG HAVE_TIFF MAPNIK_USE_PR @rem /EHsc is to support c++ exceptions - http://msdn.microsoft.com/en-us/library/1deeycx5(v=vs.80).aspx @rem /GR is to support rtti (runtime type detection) - http://msdn.microsoft.com/en-us/library/we6hfdy0.aspx if /i "%1"=="--cxxflags" echo /MD /EHsc /GR -if /i "%1"=="--includes" echo %MAPNIK_INCLUDES% +if /i "%1"=="--includes" echo %MAPNIK_INCLUDES% %MAPNIK_INCLUDES%\\agg if /i "%1"=="--input-plugins" echo %MAPNIK_INPUT_PLUGINS_DIRECTORY% if /i "%1"=="--fonts" echo %MAPNIK_FONTS_DIRECTORY% goto exit From 019873a9d2206ff13456fd274f6879951b1a80a2 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 22 May 2013 22:27:38 -0700 Subject: [PATCH 093/110] further fixes to mapnik-config.bat --- utils/mapnik-config/mapnik-config.bat | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utils/mapnik-config/mapnik-config.bat b/utils/mapnik-config/mapnik-config.bat index 929e60e45..a21f8d74c 100644 --- a/utils/mapnik-config/mapnik-config.bat +++ b/utils/mapnik-config/mapnik-config.bat @@ -26,7 +26,8 @@ if /i "%1"=="--defines" echo _WINDOWS HAVE_JPEG HAVE_PNG HAVE_TIFF MAPNIK_USE_PR @rem /EHsc is to support c++ exceptions - http://msdn.microsoft.com/en-us/library/1deeycx5(v=vs.80).aspx @rem /GR is to support rtti (runtime type detection) - http://msdn.microsoft.com/en-us/library/we6hfdy0.aspx if /i "%1"=="--cxxflags" echo /MD /EHsc /GR -if /i "%1"=="--includes" echo %MAPNIK_INCLUDES% %MAPNIK_INCLUDES%\\agg +if /i "%1"=="--includes" echo %MAPNIK_INCLUDES% %MAPNIK_INCLUDES%\\mapnik\\agg +if /i "%1"=="--dep-includes" echo if /i "%1"=="--input-plugins" echo %MAPNIK_INPUT_PLUGINS_DIRECTORY% if /i "%1"=="--fonts" echo %MAPNIK_FONTS_DIRECTORY% goto exit From 5239bef595fb02e28b06f5d0c7ea218b8a53e214 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 22 May 2013 23:23:11 -0700 Subject: [PATCH 094/110] add details for how to build rundemo.cpp standalone on windows with gyp --- demo/c++/Makefile | 9 +++++- demo/c++/README.md | 70 ++++++++++++++++++++++++++++++++++++++++++++ demo/c++/common.gypi | 42 ++++++++++++++++++++++++++ demo/c++/readme.txt | 20 ------------- demo/c++/rundemo.gyp | 41 ++++++++++++++++++++++++++ 5 files changed, 161 insertions(+), 21 deletions(-) create mode 100644 demo/c++/README.md create mode 100644 demo/c++/common.gypi delete mode 100644 demo/c++/readme.txt create mode 100644 demo/c++/rundemo.gyp diff --git a/demo/c++/Makefile b/demo/c++/Makefile index d30b71b40..dbd425380 100644 --- a/demo/c++/Makefile +++ b/demo/c++/Makefile @@ -1,4 +1,4 @@ -CXXFLAGS = $(shell mapnik-config --cflags) +CXXFLAGS = $(shell mapnik-config --includes --defines --cxxflags --dep-includes) LDFLAGS = $(shell mapnik-config --libs --dep-libs --ldflags) OBJ = rundemo.o @@ -13,8 +13,15 @@ $(BIN) : $(OBJ) .c.o : $(CXX) -c $(CXXFLAGS) $< +gyp: + rm -rf ./build + gyp rundemo.gyp --depth=. -f make --generator-output=./build/ + make -C ./build + build/out/Release/rundemo `mapnik-config --prefix` + .PHONY : clean clean: rm -f $(OBJ) rm -f $(BIN) + rm -f ./build \ No newline at end of file diff --git a/demo/c++/README.md b/demo/c++/README.md new file mode 100644 index 000000000..77e071a15 --- /dev/null +++ b/demo/c++/README.md @@ -0,0 +1,70 @@ +## rundemo.cpp + +This directory contains a simple c++ program demonstrating the Mapnik C++ API. It mimics the python 'rundemo.py' example with a couple exceptions. + +If building on unix you can have this program automatically build by configuring Mapnik like: + + ./configure DEMO=True + +However, this example code also should be able to be built standalone. + +The following notes describe how to do that on various operating systems. + +## Depends + + - Mapnik library development headers + - `mapnik-config` on unix and `mapnik-config.bat` on windows + +### Unix + +On OS X and Linux you also need `make`. + +### Windows + +On windows, additional dependencies to build are: + + - MSVS 2010 with C++ compiler + - Python 2.x + - gyp: https://code.google.com/p/gyp | https://github.com/springmeyer/hello-gyp + +`mapnik-config.bat` should come with your Mapnik installation. + +First confirm it is on your path: + + mapnik-config # should give usage + +To install gyp, which is pure python do: + + svn checkout http://gyp.googlecode.com/svn/trunk/ gyp + cd gyp + sudo python setup.py install + + +## Building the demo + +### Unix + +Simply type: + + make + +Then to run do: + + ./rundemo `mapnik-config --prefix` + +On OS X you can also create an xcode project: + + gyp rundemo.gyp --depth=. -f xcode --generator-output=./build/ + xcodebuild -project ./build/rundemo.xcodeproj + ./build/out/Release/rundemo `mapnik-config --prefix` + + +### Windows + +First you need to build he visual studio files with gyp: + + gyp rundemo.gyp --depth=. -f msvs -G msvs_version=2010 + +Then you can compile the demo with `msbuild`: + + msbuild build.sln \ No newline at end of file diff --git a/demo/c++/common.gypi b/demo/c++/common.gypi new file mode 100644 index 000000000..2dd800c78 --- /dev/null +++ b/demo/c++/common.gypi @@ -0,0 +1,42 @@ +{ + 'variables': { + 'conditions': [ + ['OS == "mac"', { + 'target_arch%': 'x64' + }, { + 'target_arch%': 'ia32' + }] + ] + }, + 'target_defaults': { + 'default_configuration': 'Release', + 'defines': [ ], + 'conditions': [ + ['OS == "mac"', { + 'defines': [ 'DARWIN' ] + }, { + 'defines': [ 'LINUX' ] + }], + ['OS == "mac" and target_arch == "x64"', { + 'xcode_settings': { + 'ARCHS': [ 'x86_64' ] + }, + }] + ], + 'configurations': { + 'Debug': { + 'cflags': [ '-g', '-O0' ], + 'xcode_settings': { + 'OTHER_CFLAGS': [ '-g', '-O0' ] + } + }, + 'Release': { + 'cflags': [ '-O3' ], + 'defines': [ 'NDEBUG' ], + 'xcode_settings': { + 'OTHER_CFLAGS': [ '-O3' ] + } + } + } + } +} \ No newline at end of file diff --git a/demo/c++/readme.txt b/demo/c++/readme.txt deleted file mode 100644 index c9d546bc5..000000000 --- a/demo/c++/readme.txt +++ /dev/null @@ -1,20 +0,0 @@ -This directory contains a simple c++ program demonstrating the Mapnik C++ API. It mimics the python 'rundemo.py' example with a couple exceptions. - -To build it re-configure SCons with DEMO=True then rebuild:: - - $ python scons/scons.py configure DEMO=True - $ python scons/scons.py - - -The sample program will be compiled (but not installed). - - -To run:: - - $ cd demo/c++ - $ ./rundemo /usr/local/lib/mapnik - -For more detailed comments have a look in demo/python/rundemo.py - -Have fun! -Artem. diff --git a/demo/c++/rundemo.gyp b/demo/c++/rundemo.gyp new file mode 100644 index 000000000..559b39363 --- /dev/null +++ b/demo/c++/rundemo.gyp @@ -0,0 +1,41 @@ +{ + 'includes': [ 'common.gypi' ], + 'include_dirs': [ + ' Date: Wed, 22 May 2013 23:23:22 -0700 Subject: [PATCH 095/110] avoid including cairo-ft in header --- include/mapnik/cairo_context.hpp | 1 - src/cairo_context.cpp | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/mapnik/cairo_context.hpp b/include/mapnik/cairo_context.hpp index db4e8b1e3..16a55e617 100644 --- a/include/mapnik/cairo_context.hpp +++ b/include/mapnik/cairo_context.hpp @@ -41,7 +41,6 @@ // cairo #include -#include // stl #include diff --git a/src/cairo_context.cpp b/src/cairo_context.cpp index 4bb569185..a85473b94 100644 --- a/src/cairo_context.cpp +++ b/src/cairo_context.cpp @@ -26,6 +26,8 @@ #include #include +#include + #include namespace mapnik { From d5bf5e86c6388f4b63e4e8648d6ed1d9b4ab8e75 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 22 May 2013 23:46:57 -0700 Subject: [PATCH 096/110] fix plugins and fonts path in mapnik-config.bat --- utils/mapnik-config/mapnik-config.bat | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/mapnik-config/mapnik-config.bat b/utils/mapnik-config/mapnik-config.bat index a21f8d74c..0b4675cba 100644 --- a/utils/mapnik-config/mapnik-config.bat +++ b/utils/mapnik-config/mapnik-config.bat @@ -4,8 +4,8 @@ set MAPNIK_VERSION=v2.2.0 set MAPNIK_PREFIX=c:\\mapnik-%MAPNIK_VERSION% set MAPNIK_LIBS=%MAPNIK_PREFIX%\\lib set MAPNIK_INCLUDES=%MAPNIK_PREFIX%\\include -set MAPNIK_INPUT_PLUGINS_DIRECTORY=%MAPNIK_PREFIX%\\input -set MAPNIK_FONTS_DIRECTORY=%MAPNIK_PREFIX%\\fonts +set MAPNIK_INPUT_PLUGINS_DIRECTORY=%MAPNIK_PREFIX%\\lib\\mapnik\\input +set MAPNIK_FONTS_DIRECTORY=%MAPNIK_PREFIX%\\lib\\mapnik\\fonts if /i "%1"=="" goto help if /i "%1"=="help" goto help From 9ede00abf8567ae254748181dc514e1212a37838 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 23 May 2013 00:15:18 -0700 Subject: [PATCH 097/110] rundemo standalone build fixes --- demo/c++/README.md | 8 +++++++- demo/c++/rundemo.gyp | 18 +++++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/demo/c++/README.md b/demo/c++/README.md index 77e071a15..187133d96 100644 --- a/demo/c++/README.md +++ b/demo/c++/README.md @@ -37,8 +37,14 @@ To install gyp, which is pure python do: svn checkout http://gyp.googlecode.com/svn/trunk/ gyp cd gyp - sudo python setup.py install + python setup.py install +If you do not have svn installed you can grab gyp from: + + https://github.com/TooTallNate/node-gyp/archive/master.zip + # unzip and extract the 'gyp' subfolder then do + cd gyp + python setup.py install ## Building the demo diff --git a/demo/c++/rundemo.gyp b/demo/c++/rundemo.gyp index 559b39363..373374f98 100644 --- a/demo/c++/rundemo.gyp +++ b/demo/c++/rundemo.gyp @@ -1,12 +1,5 @@ { 'includes': [ 'common.gypi' ], - 'include_dirs': [ - ' Date: Thu, 23 May 2013 00:46:55 -0700 Subject: [PATCH 098/110] rundemo.gyp now working on windows --- demo/c++/README.md | 11 ++++++++--- demo/c++/rundemo.gyp | 9 ++++++--- utils/mapnik-config/mapnik-config.bat | 2 +- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/demo/c++/README.md b/demo/c++/README.md index 187133d96..30f969b89 100644 --- a/demo/c++/README.md +++ b/demo/c++/README.md @@ -67,10 +67,15 @@ On OS X you can also create an xcode project: ### Windows -First you need to build he visual studio files with gyp: +First you need to build the visual studio solution with gyp: gyp rundemo.gyp --depth=. -f msvs -G msvs_version=2010 -Then you can compile the demo with `msbuild`: +Then you can compile with `msbuild`: - msbuild build.sln \ No newline at end of file + msbuild rundemo.sln /p:Configuration="Release" /p:Platform=Win32 + +Then run it! + + for /f %i in ('mapnik-config --prefix') do set MAPNIK_PREFIX=%%i + Release\rundemo.exe %MAPNIK_PREFIX% \ No newline at end of file diff --git a/demo/c++/rundemo.gyp b/demo/c++/rundemo.gyp index 373374f98..e54ae121c 100644 --- a/demo/c++/rundemo.gyp +++ b/demo/c++/rundemo.gyp @@ -1,5 +1,6 @@ { 'includes': [ 'common.gypi' ], + 'default_configuration': 'Release', 'targets': [ { 'target_name': 'rundemo', @@ -34,9 +35,11 @@ ' Date: Thu, 23 May 2013 15:20:17 -0700 Subject: [PATCH 099/110] ensure that when statically linking plugins we remove previously built ones that are stale --- SConstruct | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/SConstruct b/SConstruct index 85eca3171..49de210be 100644 --- a/SConstruct +++ b/SConstruct @@ -1797,6 +1797,9 @@ if not HELP_REQUESTED: GDAL_BUILT = False OGR_BUILT = False for plugin in env['PLUGINS']: + if env['PLUGIN_LINKING'] == 'static' or plugin not in env['REQUESTED_PLUGINS']: + if os.path.exists('plugins/input/%s.input' % plugin): + os.unlink('plugins/input/%s.input' % plugin) if plugin in env['REQUESTED_PLUGINS']: details = env['PLUGINS'][plugin] if details['lib'] in env['LIBS']: @@ -1817,8 +1820,6 @@ if not HELP_REQUESTED: color_print(1,"Notice: dependencies not met for plugin '%s', not building..." % plugin) if os.path.exists('plugins/input/%s.input' % plugin): os.unlink('plugins/input/%s.input' % plugin) - elif os.path.exists('plugins/input/%s.input' % plugin): - os.unlink('plugins/input/%s.input' % plugin) create_uninstall_target(env, env['MAPNIK_LIB_DIR_DEST'], False) create_uninstall_target(env, env['MAPNIK_INPUT_PLUGINS_DEST'] , False) From 62434041e3d32b6f271573740d2135327ce83901 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 23 May 2013 15:36:43 -0700 Subject: [PATCH 100/110] clean up hello.input if stale --- SConstruct | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/SConstruct b/SConstruct index 49de210be..01c740f61 100644 --- a/SConstruct +++ b/SConstruct @@ -1887,11 +1887,14 @@ if not HELP_REQUESTED: # if requested, build the sample input plugins if env['SAMPLE_INPUT_PLUGINS']: SConscript('plugins/input/templates/helloworld/build.py') - elif 'install' in COMMAND_LINE_TARGETS: - plugin_path = os.path.join(env['MAPNIK_INPUT_PLUGINS_DEST'],'hello.input') - if os.path.exists(plugin_path): - color_print(3,"Notice: removing out of date plugin: '%s'" % plugin_path) - os.unlink(plugin_path) + else: + if 'install' in COMMAND_LINE_TARGETS: + plugin_path = os.path.join(env['MAPNIK_INPUT_PLUGINS_DEST'],'hello.input') + if os.path.exists(plugin_path): + color_print(3,"Notice: removing out of date plugin: '%s'" % plugin_path) + os.unlink(plugin_path) + if os.path.exists('plugins/input/templates/hello.input'): + os.unlink('plugins/input/templates/hello.input') # update linux project files if env['PLATFORM'] == 'Linux': From 4057c2c822e6076dc0c33de2556f26fd0e772b57 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 23 May 2013 15:40:05 -0700 Subject: [PATCH 101/110] ensure that the input plugin directory exists even if we don't put any plugins in it --- SConstruct | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/SConstruct b/SConstruct index 01c740f61..29b6ae11c 100644 --- a/SConstruct +++ b/SConstruct @@ -1824,9 +1824,14 @@ if not HELP_REQUESTED: create_uninstall_target(env, env['MAPNIK_LIB_DIR_DEST'], False) create_uninstall_target(env, env['MAPNIK_INPUT_PLUGINS_DEST'] , False) - # before installing plugins, wipe out any previously - # installed plugins that we are no longer building if 'install' in COMMAND_LINE_TARGETS: + # if statically linking plugins still make sure + # to create the dynamic plugins directory + if env['PLUGIN_LINKING'] == 'static': + if not os.path.exists(env['MAPNIK_INPUT_PLUGINS_DEST']): + os.makedirs(env['MAPNIK_INPUT_PLUGINS_DEST']) + # before installing plugins, wipe out any previously + # installed plugins that we are no longer building for plugin in PLUGINS.keys(): plugin_path = os.path.join(env['MAPNIK_INPUT_PLUGINS_DEST'],'%s.input' % plugin) if os.path.exists(plugin_path): From fe6d860fa49f294d15c491215c8e8553b6fb4f1b Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 23 May 2013 16:06:07 -0700 Subject: [PATCH 102/110] remove mapnik-config.bat (for windows only) which is now versioned in mapnik-packaging repo: https://github.com/mapnik/mapnik-packaging/commit/173186c77b96f3b96b70112da33ad35eecbe34db --- utils/mapnik-config/mapnik-config.bat | 51 --------------------------- 1 file changed, 51 deletions(-) delete mode 100644 utils/mapnik-config/mapnik-config.bat diff --git a/utils/mapnik-config/mapnik-config.bat b/utils/mapnik-config/mapnik-config.bat deleted file mode 100644 index 4b48b06b7..000000000 --- a/utils/mapnik-config/mapnik-config.bat +++ /dev/null @@ -1,51 +0,0 @@ -@echo off - -set MAPNIK_VERSION=v2.2.0 -set MAPNIK_PREFIX=c:\\mapnik-%MAPNIK_VERSION% -set MAPNIK_LIBS=%MAPNIK_PREFIX%\\lib -set MAPNIK_INCLUDES=%MAPNIK_PREFIX%\\include -set MAPNIK_INPUT_PLUGINS_DIRECTORY=%MAPNIK_PREFIX%\\lib\\mapnik\\input -set MAPNIK_FONTS_DIRECTORY=%MAPNIK_PREFIX%\\lib\\mapnik\\fonts - -if /i "%1"=="" goto help -if /i "%1"=="help" goto help -if /i "%1"=="--help" goto help -if /i "%1"=="-help" goto help -if /i "%1"=="/help" goto help -if /i "%1"=="?" goto help -if /i "%1"=="-?" goto help -if /i "%1"=="--?" goto help -if /i "%1"=="/?" goto help -if /i "%1"=="--prefix" echo %MAPNIK_PREFIX% -if /i "%1"=="--libs" echo mapnik.lib -@rem TODO - figure out how to avoid hardcoding these library names -if /i "%1"=="--dep-libs" echo icuuc.lib icuin.lib cairo.lib libboost_system-vc100-mt-s-1_49.lib -if /i "%1"=="--ldflags" echo %MAPNIK_LIBS% -if /i "%1"=="--defines" echo _WINDOWS HAVE_JPEG HAVE_PNG HAVE_TIFF MAPNIK_USE_PROJ4 BOOST_REGEX_HAS_ICU MAPNIK_THREADSAFE BIGINT HAVE_LIBXML2 HAVE_CAIRO -@rem /MD is multithreaded dynamic linking - http://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx -@rem /EHsc is to support c++ exceptions - http://msdn.microsoft.com/en-us/library/1deeycx5(v=vs.80).aspx -@rem /GR is to support rtti (runtime type detection) - http://msdn.microsoft.com/en-us/library/we6hfdy0.aspx -if /i "%1"=="--cxxflags" echo /MD /EHsc /GR -if /i "%1"=="--includes" echo %MAPNIK_INCLUDES% %MAPNIK_INCLUDES%\\mapnik\\agg -if /i "%1"=="--dep-includes" echo -if /i "%1"=="--input-plugins" echo %MAPNIK_INPUT_PLUGINS_DIRECTORY% -if /i "%1"=="--fonts" echo %MAPNIK_FONTS_DIRECTORY% -goto exit - - -:help -echo mapnik-config.bat -echo Examples: -echo --libs : provide lib name for mapnik.dll -echo --defines : provide compiler defines needed for this mapnik build -echo --dep-libs : provide lib names of depedencies -echo --ldflags : provide lib paths to depedencies -echo --cxxflags : provide compiler flags -echo --includes : provide header paths for mapnik -echo --dep-includes : provide header paths for dependencies -echo --input-plugins : provide path to input plugins directory -echo --fonts : provide path to fonts directory -goto exit - -:exit -goto :EOF From d0671098d1428a10565ae015ca37568e638c15d7 Mon Sep 17 00:00:00 2001 From: artemp Date: Fri, 24 May 2013 09:32:48 +0100 Subject: [PATCH 103/110] OGR.input + set tolerance tol=0.001 in features_at_point test older versions of GDAL are not handling empty bounding boxes correctly e.g SetSpatialFilterRect(x0,y0,x0,y0) tested on ubuntu-12.04/64-bit g++-4.6.3 gdal-1.7.3 --- tests/python_tests/ogr_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/python_tests/ogr_test.py b/tests/python_tests/ogr_test.py index baf982bd7..4a0acc659 100644 --- a/tests/python_tests/ogr_test.py +++ b/tests/python_tests/ogr_test.py @@ -30,7 +30,7 @@ if 'ogr' in mapnik.DatasourceCache.plugin_names(): # See SHAPE_ENCODING for overriding: http://gdal.org/ogr/drv_shapefile.html # So: failure for the NOM_FR field is expected for older gdal ds = mapnik.Ogr(file='../../demo/data/boundaries.shp',layer_by_index=0) - f = ds.features_at_point(ds.envelope().center()).features[0] + f = ds.features_at_point(ds.envelope().center(), 0.001).features[0] eq_(ds.geometry_type(),mapnik.DataGeometryType.Polygon) eq_(f['CGNS_FID'], u'6f733341ba2011d892e2080020a0f4c9') From 2291c8b698a140222ccb64d4f4081593a16e975a Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Fri, 24 May 2013 12:46:51 -0700 Subject: [PATCH 104/110] disable hidden visibility for plugins and hidden inlines for the library by default for 2.2 release - can re-enable after more testing - refs #1863 --- SConstruct | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SConstruct b/SConstruct index 29b6ae11c..2a52d5c87 100644 --- a/SConstruct +++ b/SConstruct @@ -1650,7 +1650,9 @@ if not preconfigured: if env['DEBUG']: env.Append(CXXFLAGS = common_cxx_flags + '-O0 -fno-inline') else: - env.Append(CXXFLAGS = common_cxx_flags + '-O%s -fvisibility-inlines-hidden -fno-strict-aliasing -finline-functions -Wno-inline -Wno-parentheses -Wno-char-subscripts' % (env['OPTIMIZATION'])) + # TODO - add back -fvisibility-inlines-hidden + # https://github.com/mapnik/mapnik/issues/1863 + env.Append(CXXFLAGS = common_cxx_flags + '-O%s -fno-strict-aliasing -finline-functions -Wno-inline -Wno-parentheses -Wno-char-subscripts' % (env['OPTIMIZATION'])) if env['DEBUG_UNDEFINED']: env.Append(CXXFLAGS = '-fsanitize=undefined-trap -fsanitize-undefined-trap-on-error -ftrapv -fwrapv') @@ -1765,8 +1767,6 @@ if not HELP_REQUESTED: Export('env') plugin_base = env.Clone() - if not env['DEBUG']: - plugin_base.Append(CXXFLAGS='-fvisibility=hidden') Export('plugin_base') From d1d688bb8ac66c4e783dca10b80cbd5f370499ba Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Fri, 24 May 2013 13:26:52 -0700 Subject: [PATCH 105/110] fix typo --- demo/c++/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/c++/README.md b/demo/c++/README.md index 30f969b89..d18d49ef4 100644 --- a/demo/c++/README.md +++ b/demo/c++/README.md @@ -77,5 +77,5 @@ Then you can compile with `msbuild`: Then run it! - for /f %i in ('mapnik-config --prefix') do set MAPNIK_PREFIX=%%i + for /f %i in ('mapnik-config --prefix') do set MAPNIK_PREFIX=%i Release\rundemo.exe %MAPNIK_PREFIX% \ No newline at end of file From db7e5919d271c668a65e4fa6896a41efdcce2ddb Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Fri, 24 May 2013 14:29:11 -0600 Subject: [PATCH 106/110] Fix example of building vs files with gyp on windows --- demo/c++/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/demo/c++/README.md b/demo/c++/README.md index d18d49ef4..daa4198c1 100644 --- a/demo/c++/README.md +++ b/demo/c++/README.md @@ -69,7 +69,7 @@ On OS X you can also create an xcode project: First you need to build the visual studio solution with gyp: - gyp rundemo.gyp --depth=. -f msvs -G msvs_version=2010 + C:\Python27\python.exe c:\Python27\Scripts\gyp rundemo.gyp --depth=. -f msvs -G msvs_version=2010 Then you can compile with `msbuild`: @@ -78,4 +78,4 @@ Then you can compile with `msbuild`: Then run it! for /f %i in ('mapnik-config --prefix') do set MAPNIK_PREFIX=%i - Release\rundemo.exe %MAPNIK_PREFIX% \ No newline at end of file + Release\rundemo.exe %MAPNIK_PREFIX% From 7302b3b65492e0b9e7106f9887bbbefe101f4728 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Fri, 24 May 2013 16:45:02 -0700 Subject: [PATCH 107/110] fix #1865 --- INSTALL.md | 2 +- include/mapnik/filter_factory.hpp | 6 ++++++ include/mapnik/palette.hpp | 1 - plugins/input/sqlite/sqlite_connection.hpp | 2 -- src/cairo_context.cpp | 1 - src/cairo_renderer.cpp | 5 ----- src/projection.cpp | 4 ++++ 7 files changed, 11 insertions(+), 10 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index fd5300cbf..156f9b3dc 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -65,7 +65,7 @@ Note: Python3k is supported, see: https://github.com/mapnik/mapnik/wiki/Python3k Optional dependencies: - * Cairo - Graphics library for output formats like PDF, PS, and SVG + * Cairo >= 1.6.0 - Graphics library for output formats like PDF, PS, and SVG - pkg-config - Required for building with cairo support - pycairo - Python bindings for cairo * libpq - PostgreSQL libraries (For PostGIS plugin support) diff --git a/include/mapnik/filter_factory.hpp b/include/mapnik/filter_factory.hpp index 1150ece5d..68d729a6c 100644 --- a/include/mapnik/filter_factory.hpp +++ b/include/mapnik/filter_factory.hpp @@ -1,2 +1,8 @@ +// TODO - remove this file at mapnik 3.x +#ifdef _MSC_VER +#pragma NOTE("filter_factory.hpp" is now called "expression.hpp") +#else #warning "filter_factory.hpp" is now called "expression.hpp" +#endif + #include diff --git a/include/mapnik/palette.hpp b/include/mapnik/palette.hpp index 3598a2b08..972cf56e3 100644 --- a/include/mapnik/palette.hpp +++ b/include/mapnik/palette.hpp @@ -34,7 +34,6 @@ #include typedef google::dense_hash_map rgba_hash_table; #else - #warning compiling without dense_hash_map #include typedef boost::unordered_map rgba_hash_table; #endif diff --git a/plugins/input/sqlite/sqlite_connection.hpp b/plugins/input/sqlite/sqlite_connection.hpp index 7518f816b..54908434b 100644 --- a/plugins/input/sqlite/sqlite_connection.hpp +++ b/plugins/input/sqlite/sqlite_connection.hpp @@ -62,7 +62,6 @@ public: #endif const int rc = sqlite3_open_v2 (file_.c_str(), &db_, mode, 0); #else -#warning "Mapnik's sqlite plugin is compiling against a version of sqlite older than 3.5.x which may make rendering slow..." const int rc = sqlite3_open (file_.c_str(), &db_); #endif if (rc != SQLITE_OK) @@ -83,7 +82,6 @@ public: #if SQLITE_VERSION_NUMBER >= 3005000 const int rc = sqlite3_open_v2 (file_.c_str(), &db_, flags, 0); #else -#warning "Mapnik's sqlite plugin is compiling against a version of sqlite older than 3.5.x which may make rendering slow..." const int rc = sqlite3_open (file_.c_str(), &db_); #endif if (rc != SQLITE_OK) diff --git a/src/cairo_context.cpp b/src/cairo_context.cpp index a85473b94..cf04116f3 100644 --- a/src/cairo_context.cpp +++ b/src/cairo_context.cpp @@ -155,7 +155,6 @@ void cairo_context::set_operator(composite_mode_e comp_op) cairo_set_operator(cairo_.get(), CAIRO_OPERATOR_EXCLUSION); break; #else -#warning building against cairo older that 1.10.0, some compositing options are disabled case multiply: case screen: case overlay: diff --git a/src/cairo_renderer.cpp b/src/cairo_renderer.cpp index 6dcd628b8..d0305b5d7 100644 --- a/src/cairo_renderer.cpp +++ b/src/cairo_renderer.cpp @@ -258,14 +258,9 @@ void cairo_renderer_base::setup(Map const& map) void cairo_renderer_base::start_map_processing(Map const& map) { MAPNIK_LOG_DEBUG(cairo_renderer) << "cairo_renderer_base: Start map processing bbox=" << map.get_current_extent(); - -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 6, 0) box2d bounds = t_.forward(t_.extent()); context_.rectangle(bounds.minx(), bounds.miny(), bounds.maxx(), bounds.maxy()); context_.clip(); -#else -#warning building against cairo older that 1.6.0, map clipping is disabled -#endif } template <> diff --git a/src/projection.cpp b/src/projection.cpp index 53ffce126..57dcdb243 100644 --- a/src/projection.cpp +++ b/src/projection.cpp @@ -31,7 +31,11 @@ #include #if defined(MAPNIK_THREADSAFE) && PJ_VERSION < 480 #include +#ifdef _MSC_VER +#pragma NOTE(mapnik is building against < proj 4.8, reprojection will be faster if you use >= 4.8) +#else #warning mapnik is building against < proj 4.8, reprojection will be faster if you use >= 4.8 +#endif static boost::mutex mutex_; #endif From b86303202ee43bb3a2242c6f5f260a8efb4db369 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Fri, 24 May 2013 16:47:18 -0700 Subject: [PATCH 108/110] remove a few seemingly valid pngs that no longer fail with libpng > 1.6 - closes #1854 --- tests/data/pngsuite/xcsn0g01.png | Bin 164 -> 0 bytes tests/data/pngsuite/xhdn0g08.png | Bin 138 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 tests/data/pngsuite/xcsn0g01.png delete mode 100644 tests/data/pngsuite/xhdn0g08.png diff --git a/tests/data/pngsuite/xcsn0g01.png b/tests/data/pngsuite/xcsn0g01.png deleted file mode 100644 index 9863a262caaa53077f5023d5a7a8df6bc753880e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 164 zcmeAS@N?(olHy`uVBq!ia0vp^3Lwk~Bp9L@-6Me%OS+@4BLidG0>c;6;z7cmE{-7; zb9B#amdK II;Vst0B-g)L;wH) diff --git a/tests/data/pngsuite/xhdn0g08.png b/tests/data/pngsuite/xhdn0g08.png deleted file mode 100644 index fcb8737fa2505b43955e995d95c3d6972b85fe32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 138 zcmeAS@N?(olHy`uVBq!ia0vp^3Lwk@Bp957Lw$i1OS+@4BLidG0>c;6;(>fePZ!6K ziaE(C2`UUC949vOu(I~>b7=nfE^nVuX&@tFa7u7Oddi~87#=p(p8gM~{{27yqy1T- hp#)Io!PL|g7KVVQ{|ZeX-G~HP;_2$=vd$@?2>>eWDsTV* From 1211211f98f7dbaf8810cb3b695567a286af244c Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Fri, 24 May 2013 17:21:55 -0700 Subject: [PATCH 109/110] quiet mode for c++ tests --- tests/cpp_tests/agg_blend_src_over_test.cpp | 13 +++++++++++-- tests/cpp_tests/clipping_test.cpp | 12 ++++++++++-- tests/cpp_tests/conversions_test.cpp | 13 +++++++++++-- tests/cpp_tests/csv_parse_test.cpp | 14 +++++++++++--- tests/cpp_tests/exceptions_test.cpp | 13 +++++++++++-- tests/cpp_tests/font_registration_test.cpp | 13 +++++++++++-- tests/cpp_tests/fontset_runtime_test.cpp | 13 +++++++++++-- tests/cpp_tests/geometry_converters_test.cpp | 13 +++++++++++-- tests/cpp_tests/image_io_test.cpp | 14 +++++++++++--- tests/cpp_tests/label_algo_test.cpp | 13 +++++++++++-- tests/cpp_tests/map_request_test.cpp | 13 +++++++++++-- tests/cpp_tests/params_test.cpp | 13 ++++++++++--- tests/cpp_tests/wkb_formats_test.cpp | 13 +++++++++++-- 13 files changed, 141 insertions(+), 29 deletions(-) diff --git a/tests/cpp_tests/agg_blend_src_over_test.cpp b/tests/cpp_tests/agg_blend_src_over_test.cpp index 8f0427bb9..d3014df5a 100644 --- a/tests/cpp_tests/agg_blend_src_over_test.cpp +++ b/tests/cpp_tests/agg_blend_src_over_test.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "agg_color_rgba.h" #include "agg_pixfmt_rgba.h" #include "agg_rendering_buffer.h" @@ -130,8 +131,15 @@ template struct comp_op_rgba_src_over2 } -int main( int, char*[] ) +int main(int argc, char** argv) { + std::vector args; + for (int i=1;i source_over_old_agg; typedef agg::comp_op_rgba_src_over source_over; @@ -194,7 +202,8 @@ int main( int, char*[] ) */ if (!::boost::detail::test_errors()) { - std::clog << "C++ AGG blending: \x1b[1;32m✓ \x1b[0m\n"; + if (quiet) std::clog << "\x1b[1;32m.\x1b[0m"; + else std::clog << "C++ AGG blending: \x1b[1;32m✓ \x1b[0m\n"; #if BOOST_VERSION >= 104600 ::boost::detail::report_errors_remind().called_report_errors_function = true; #endif diff --git a/tests/cpp_tests/clipping_test.cpp b/tests/cpp_tests/clipping_test.cpp index 457cacfec..655c0c4d2 100644 --- a/tests/cpp_tests/clipping_test.cpp +++ b/tests/cpp_tests/clipping_test.cpp @@ -76,8 +76,15 @@ void parse_geom(mapnik::geometry_type & geom, } } -int main( int, char*[] ) +int main(int argc, char** argv) { + std::vector args; + for (int i=1;i= 104600 ::boost::detail::report_errors_remind().called_report_errors_function = true; #endif diff --git a/tests/cpp_tests/conversions_test.cpp b/tests/cpp_tests/conversions_test.cpp index 023d7f703..3be55b73d 100644 --- a/tests/cpp_tests/conversions_test.cpp +++ b/tests/cpp_tests/conversions_test.cpp @@ -3,9 +3,17 @@ #include #include #include +#include -int main( int, char*[] ) +int main(int argc, char** argv) { + std::vector args; + for (int i=1;i= 104600 ::boost::detail::report_errors_remind().called_report_errors_function = true; #endif diff --git a/tests/cpp_tests/csv_parse_test.cpp b/tests/cpp_tests/csv_parse_test.cpp index edd0835d9..cf4aa22db 100644 --- a/tests/cpp_tests/csv_parse_test.cpp +++ b/tests/cpp_tests/csv_parse_test.cpp @@ -3,10 +3,17 @@ #include #include "plugins/input/csv/csv_datasource.hpp" #include +#include - -int main( int, char*[] ) +int main(int argc, char** argv) { + std::vector args; + for (int i=1;i= 104600 ::boost::detail::report_errors_remind().called_report_errors_function = true; #endif diff --git a/tests/cpp_tests/exceptions_test.cpp b/tests/cpp_tests/exceptions_test.cpp index 2841a43ee..c56aae050 100644 --- a/tests/cpp_tests/exceptions_test.cpp +++ b/tests/cpp_tests/exceptions_test.cpp @@ -19,13 +19,21 @@ #include #include #include +#include extern "C" { #include } -int main( int, char*[] ) +int main(int argc, char** argv) { + std::vector args; + for (int i=1;i= 104600 ::boost::detail::report_errors_remind().called_report_errors_function = true; #endif diff --git a/tests/cpp_tests/font_registration_test.cpp b/tests/cpp_tests/font_registration_test.cpp index 41908c21d..6c7ac84fe 100644 --- a/tests/cpp_tests/font_registration_test.cpp +++ b/tests/cpp_tests/font_registration_test.cpp @@ -7,9 +7,17 @@ namespace sys = boost::system; #include #include #include +#include -int main( int, char*[] ) +int main(int argc, char** argv) { + std::vector args; + for (int i=1;i= 104600 ::boost::detail::report_errors_remind().called_report_errors_function = true; #endif diff --git a/tests/cpp_tests/fontset_runtime_test.cpp b/tests/cpp_tests/fontset_runtime_test.cpp index ca587620e..e7599e8ec 100644 --- a/tests/cpp_tests/fontset_runtime_test.cpp +++ b/tests/cpp_tests/fontset_runtime_test.cpp @@ -17,10 +17,18 @@ #include #include #include +#include -int main( int, char*[] ) +int main(int argc, char** argv) { + std::vector args; + for (int i=1;i(); @@ -61,7 +69,8 @@ int main( int, char*[] ) BOOST_TEST_EQ(std::string(ex.what()),std::string("No valid font face could be loaded for font set: 'fontset'")); } if (!::boost::detail::test_errors()) { - std::clog << "C++ fontset runtime: \x1b[1;32m✓ \x1b[0m\n"; + if (quiet) std::clog << "\x1b[1;32m.\x1b[0m"; + else std::clog << "C++ fontset runtime: \x1b[1;32m✓ \x1b[0m\n"; #if BOOST_VERSION >= 104600 ::boost::detail::report_errors_remind().called_report_errors_function = true; #endif diff --git a/tests/cpp_tests/geometry_converters_test.cpp b/tests/cpp_tests/geometry_converters_test.cpp index fbf60a5c0..3ba82c85d 100644 --- a/tests/cpp_tests/geometry_converters_test.cpp +++ b/tests/cpp_tests/geometry_converters_test.cpp @@ -17,6 +17,7 @@ #include #include +#include struct output_geometry_backend { @@ -122,8 +123,15 @@ boost::optional polygon_bbox_clipping(mapnik::box2d bbox, return boost::optional(); } -int main( int, char*[] ) +int main(int argc, char** argv) { + std::vector args; + for (int i=1;i= 104600 ::boost::detail::report_errors_remind().called_report_errors_function = true; #endif diff --git a/tests/cpp_tests/image_io_test.cpp b/tests/cpp_tests/image_io_test.cpp index ea51b1d70..0d3e10be1 100644 --- a/tests/cpp_tests/image_io_test.cpp +++ b/tests/cpp_tests/image_io_test.cpp @@ -8,10 +8,17 @@ namespace sys = boost::system; #include #include #include +#include - -int main( int, char*[] ) +int main(int argc, char** argv) { + std::vector args; + for (int i=1;i type; try @@ -80,7 +87,8 @@ int main( int, char*[] ) } if (!::boost::detail::test_errors()) { - std::clog << "C++ image i/o: \x1b[1;32m✓ \x1b[0m\n"; + if (quiet) std::clog << "\x1b[1;32m.\x1b[0m"; + else std::clog << "C++ image i/o: \x1b[1;32m✓ \x1b[0m\n"; #if BOOST_VERSION >= 104600 ::boost::detail::report_errors_remind().called_report_errors_function = true; #endif diff --git a/tests/cpp_tests/label_algo_test.cpp b/tests/cpp_tests/label_algo_test.cpp index e2d36328b..abd6b604c 100644 --- a/tests/cpp_tests/label_algo_test.cpp +++ b/tests/cpp_tests/label_algo_test.cpp @@ -3,10 +3,18 @@ #include #include #include +#include -int main( int, char*[] ) +int main(int argc, char** argv) { + std::vector args; + for (int i=1;i= 104600 ::boost::detail::report_errors_remind().called_report_errors_function = true; #endif diff --git a/tests/cpp_tests/map_request_test.cpp b/tests/cpp_tests/map_request_test.cpp index d14474750..6bf777514 100644 --- a/tests/cpp_tests/map_request_test.cpp +++ b/tests/cpp_tests/map_request_test.cpp @@ -18,6 +18,7 @@ #include #include #include +#include bool compare_images(std::string const& src_fn,std::string const& dest_fn) { @@ -56,8 +57,15 @@ bool compare_images(std::string const& src_fn,std::string const& dest_fn) return true; } -int main( int, char*[] ) +int main(int argc, char** argv) { + std::vector args; + for (int i=1;i= 104600 ::boost::detail::report_errors_remind().called_report_errors_function = true; #endif diff --git a/tests/cpp_tests/params_test.cpp b/tests/cpp_tests/params_test.cpp index fe2e717dd..a262d9005 100644 --- a/tests/cpp_tests/params_test.cpp +++ b/tests/cpp_tests/params_test.cpp @@ -4,11 +4,17 @@ #include #include #include - +#include #include -int main( int, char*[] ) +int main(int argc, char** argv) { + std::vector args; + for (int i=1;i("null")/* && *params.get("null") == mapnik::value_null()*/) ); if (!::boost::detail::test_errors()) { - std::clog << "C++ parameters: \x1b[1;32m✓ \x1b[0m\n"; + if (quiet) std::clog << "\x1b[1;32m.\x1b[0m"; + else std::clog << "C++ parameters: \x1b[1;32m✓ \x1b[0m\n"; #if BOOST_VERSION >= 104600 ::boost::detail::report_errors_remind().called_report_errors_function = true; #endif diff --git a/tests/cpp_tests/wkb_formats_test.cpp b/tests/cpp_tests/wkb_formats_test.cpp index e1dd8402f..10715ec3f 100644 --- a/tests/cpp_tests/wkb_formats_test.cpp +++ b/tests/cpp_tests/wkb_formats_test.cpp @@ -5,10 +5,18 @@ #include #include #include +#include -int main( int, char*[] ) +int main(int argc, char** argv) { + std::vector args; + for (int i=1;i= 104600 ::boost::detail::report_errors_remind().called_report_errors_function = true; #endif From 8f84146948aa4f3639cd9c1ea460851b4c7f71f0 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Fri, 24 May 2013 17:22:28 -0700 Subject: [PATCH 110/110] run in quiet mode --- run_tests | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/run_tests b/run_tests index 329c8e70d..8e7121b5d 100755 --- a/run_tests +++ b/run_tests @@ -8,10 +8,12 @@ failures=$((failures+$?)) echo "*** Running C++ tests..." for FILE in tests/cpp_tests/*-bin; do - ${FILE}; + ${FILE} -q; failures=$((failures+$?)) done +echo + echo "*** Running python tests..." python tests/run_tests.py -q failures=$((failures+$?))

}z(e$Fdl`WO8R< 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 Eg6EpnOV`?dt@am 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 o3^Y%sz8i5X-3#Eso~m>4zxwvYs;8gowR)lV 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 z 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 literal 27238 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%0A6WgQjtVCqMP>;^TF} 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 A4FCWD literal 14030 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 diff --git a/tests/visual_tests/images/line-offset-900-250-2.0-cairo-reference.png b/tests/visual_tests/images/line-offset-900-250-2.0-cairo-reference.png index c365880762b96c0bf9223fd964cba61ffb12b838..b83bd731f2877722d1135982a5c2f1f9dcbc3cfe 100644 GIT binary patch literal 14477 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 literal 14527 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 diff --git a/tests/visual_tests/images/lines-1-200-200-1.0-cairo-reference.png b/tests/visual_tests/images/lines-1-200-200-1.0-cairo-reference.png index dae174f2299ddd6b7342b342dcd3bce38f902d6f..b2d300f0f87791bb77aa0abae9241b5a90038733 100644 GIT binary patch delta 1699 zcmV;U23+~R54sPKBmw`CC69j*5&sqz{}~zo9UcE6A^#;M|0yZ|E-wEtF#k3-|2#bZ zK|%jTMgL4p|4>l>RaO67T>oQZ|7vRgZf^f`bN_yR|A2u1ii-b|lK-2V|DvM*tgOt; z%>T2q|GT@=($fFL#Q)69|I^d|-QEA>+4&iI;23alaH?~MG_^wP9!U*Bf(8_IOM`sC=hcv8NCpR`LoBmf5_6xZf09LARfN; z%zijnBt_w5dT-O8PG`(@((&{`Di|9Ur7|&JGMe7kJ{AJeboPhOnTZ!7`H-{}OBJ+E zN?G!{94B%i;#Tk_NKsm5rj}7>NLI>^ii%Q58yu-9Z?0&^CA#s?MdVU?ds*_6BcAYS zA2(z2F;O6_uaZt4f15GWDhx8&^WL!r+@JF`eaWZ0~qVSFuM#>c>-w^-Th{!1^XLPb8CAF=M6_Low53-S; z5-CZ$bL`#=ka(T1J>T02s2~CS+rLFvNA)RtB@?3Sq&#*+;D3Yz9_IWExUUvPG z6g(UkRpS9Eq}QI*IU?;shti>56#hwGj-;b%^-v2K+;h~4Co2)he7?OYQ-yDTZQK3j zRrxzZV;LBG5n)XjqC2T8oEeuC>^p z2sBUx8Ylt{O5~-ncyz5q9>4lFJqK+R0S1ad14W>LBG5n)XjqE8zbqaDE0M+KJBobu ze4huCd;uJP<9my|1LL(>{X^bfes}nFEJZ-^(YhoDysW6|tt?-3>>D@|dA+TClDFIT z^p6xKRqkiuQ@kJLxu#E@jyf2=pSAeAbSl!ltH{GuFhBq!ms=~-MZ$+m5iMnk1$i0=? zirD>sUet7LcPVnsRJ&|{Dy>tdsLg9>D{^krrrd~WE-lb^3LmrdR=uoJnWgWQig@X8eSZ6Y|}ojGWVW$`1Yt&K>R z8S8QA0bP3Ujf6&nj%)ldTD_o(e{tH{h}7tR(8f=d>2tE`USJF}hk8bNi&IV`7U^4$ zHJe8W5+H{#bBG!dCfR$kZYvh~pd-|cawh${^FTp|z8xB=E}QIe&UTxrMPH##jbi2` zIq|?i$9fJi|F$%h-RanfFtSS(x%re%0 zbMc1`EZ5)3jjYb>jGJuP-AC$Fkn(u2nX3zLtmnd<{N9)|J?@poPK3?ku3p9qH3siC z5gZ|WW{?FLihZtSdh)!goAS{=UV5nNoh-z~^GH)1*?fPxtIPubL9Z*^**UkJy+}_O zd1|cd;_5nrB`V?LkNGjqiHP_inz|%^dn6)SA#k@G^dyL^t=D-h(ib%2K2^;mG7vE;vJG66x_*Hm1kg3lY`)6sjGvN1Fs< zrbX;-&8jy>HsrYoIaTODCr$gu&>2_b-y3CfXKK$@-SJAq*k!qHzB0NIA(_cR1D!ow zv^h)s-ieHMS?kz*DKy5xH&&A+rs@Mfzw%OqsLY6{H$gamji;sA$VhAOZB tLmxZ=>bJU%L6dO-I2;WWfd+~|<3E?l#XXi7M3(>n002ovPDHLkV1m~PR(${f delta 1703 zcmV;Y23YyJ555nOBmw!6C69j+68{z!{~8+q9UcE6A^#~U|1K{7Ffji)IsZXH|3yXr zOice#QU6?A|6*eQYHI&(ZvS(0|9yS`fr0;vi~p08|DB!xqN4w;t^cyJ%*@RHySviT z(*MN7|IEz))6@Uk+yCU`|LExd?(YBp|EvU>Bme*gfJsC_RCt{2n+ZKv;yM(73$lzj zh#P`~0wOS2WN>hF*jxYscPan>$GnuSN!z3??Hezg`<)}SImsnolBQ`8X}l&#UtIyO zlY9p#lZ^)ve~on%QASJh{V-h@7a0xmX~$Uy$YFy?iVX3;VXW_osPC z!YofdzPS-elxBG%SvegE?vmz^3;Ur!%;8}4LL}zTf1b}oHa3qk2hu6=@NH*K!oea* z3MbRYyZ&@KqtBC$r%zMC*d!^HiTRSz^oe>}2t?D_J)biZFGTVoX(N^@sFjqm#e z$c2bo!IvOOX_*;XTAm46DL+jrN+Gp#q@ujLskTdW=bwwnrS!o!$xm85;Zsj{WAZss zAZ%}ue@dR~MCN%&-P1>jLn$*$3LcP`Qz>`ukLL-8fqyTek_&H?W*7T@lr(QjJvs=@ zl4y~69{JQCIf%zek4=-phiVupSCo7c{9hv?r=*-#$&!>*k9IaiBAa{UAa4*UNk>b3 zycZ(VD?OqWM<3Z+cQD3X=-^XG+h%Du>A)e%3!5@DxEj)Lm>ew4f%`6VfMHczU? z15#*~dy(fSDHl4E&SpvBALQky^s`z$Qv(K%9aZANOvEvtPfv)B>9hW$Svf8Yp$XAc|Ws`gd z9e>ta)-VFc}zv@VECv0|BH{#6>0GDXgqGpa;;}wz>&zXdGSf!nq9un z;MNYG;?Gg~_40J+^u`~P_gc@Y3GIsvTNQb-Yy2$B7k9j>+}ax2@0`*TUfY#L#+z|_ zc2w<%Rcwm9-Ca)OU5&?e^}XCOuZ(+<(SHS<^SQh@EVn*fiYUDsst)gqxMtOHFVbOh zR_T*Vmm+OC*nLYoZoGOgqWlt+dv+!AS*BxEnX`dDvulyAs9ejHi0GTFrarT4k>8?n z7yJv}T12S`BA4}%-HJSl3--AMtwlP*$ogBATahbq!TRzdN>v#7##<4?KT(%;r+?gA zsilb3??rWUyGxO4Cfil_bJSsHu`aKqrAW=9P8}~rp6N`#rjxtUp)bU8AA&sXp4w1Jh#A3DhGXp%Ny5{);-rFml-ZDg8!&nasmGGzLC z-jMHf>a8~tDlIy$_QPmqD*MeTYkwhfLx1%Cd zGwX<NNF+}wUv)FquFDn-Lpd&Q2^btM18}iIQbEe|&Sqv84=)kp;2YTkD-m`T_oqMKsL^<{h~Nl? zd0;ofU7=X7wRBIOe;T^=D7UYdqbUn<@jTKNN7mn;niqE9Kj;mGA6Dbn(^BMB7}?O* z4GZrnf-Nf1-h<&W&WVV{I)7fj``}vSPEgFUMpydWu0=#6fcsQsOXNckS$_0f zWZdBnu8OFbjqRXoUSPAnb}uqkx|gPfYKB&GDDuoVG4{0wB7{f7W=f?k*k!aSLR9Kz zwUNtpV}-mCA(sjrNPWV#!tQRo@kVvi8<}MEN`!o5Yj*3c_w8i2{Yw4F=N-LzxbAT_ zn7k7q4+})4_F!{O>fjqIWj>!=-t&WdDMDsU`wKZoU%ja+<)&c=p2k}d2RQvQbYN8n x#STkXm(M!>T4A+8lYIv#lZ^)y8OGX({0Gr`=_OaW$l?G1002ovPDHLkV1hrHTD$-N diff --git a/tests/visual_tests/images/lines-1-200-200-2.0-cairo-reference.png b/tests/visual_tests/images/lines-1-200-200-2.0-cairo-reference.png index d97b38ca2816073d8a03efda61bd79ed6ea66314..81727a68e5120b167465e0f068c8373e2dd6960f 100644 GIT binary patch literal 1149 zcma)+Ycv}M6vtD{BIXF{SlLL6uGOdoT`Gi%h&VE|S~kM4FjI2n0gM zlS)3L(t*7~s;hW4Kcr42$-cC+?tnmmrY2}w}>o7#oABDVUpsr6pjofWrZS z0M5=pp@5ec`1-=Rb3msZb@)VGi}T1iq@k1M>Sg-HAU##NY~;w!d2wGW(YX$>Mw%F73hZ7V&s7Y z*%yWqYR=%2b1iojx$o6duRJGdhQHo(i-D@u=Ay;y|HsG!etU^iIZlsM7MX$CcSz-B zlUk$jv1?`#G-^44eZ|ZmE|?45=q`Rzde)>?2OKw zWC#emcZt#%ytHy>@gybh7f(O-*y`_h`w!mnFrDD!W}plnv?PQjBb!FV^8OHt}`` z!npNB`}INMtmLCWs-%t7>e8FYieoG9G`wH8K%3%VOD;`v=-9d5BzyMGGKaBgIqUN? zlNTQ9z~92@eIs#HawLgS@B^eA_XB; z6`e|bOwha}a(D_u?xH!gOSO12-fa8n@&ax2itW5rt)^g%?!x`N7d>w~zs8`V$ZfRI zLRBlXoj-3~u6Ek`u+V1VI4SW#**!Veg4)>SG(I|8gE1Mhbm)*WdGRkzce51QwOAeD zK&4G4twgLTB;9A_WNgN_ut*8y-E!BQ&%-mMIaFB z0sh`$avggY%8GI}RwUe$OAhgDgbyGPaPT0gs{;xJy1JmR55~rT!GO6r*w_FL2k!3R z;Q^kW;Oh%PK|mxzWF*AJK}rgwrvrro+1XH72&JV^RRy)R(9{GB1~8exX2ZY$OiTce z2YfyV1RxZGSPTFOV-^$yLXjKb?HO^oX??yhoQ>BRNq&G1cM3l-!|5s$1M2%mZ~oAw zNjs+n$8GUchZ3t2O)bP=^TK%20~}qmbX3XFPl6OxCx*m6{??-ryLD~|2`v7gT zpI9~4ZC|Lc*WPw17-%cJAKIXtE0m}GwWb5_s`($=w9P`|k455-s9bs}4OMc+AZ<{R zW9X==!G$&Q|9#9Num00rbY;&fEzW!7DdvjR+D(x*A20uK1_mj2;5yOC!apZvvso@A z)*MXoqZ^)a{LU~(Xj-l-nyq%>+iBhBX`)+|)a10*Un`b)aeAUs(wwbjt1bR8pm$Ts zY@Wxm27@#z78N~6=SziCIk+2@r`{L`NRmrV8T1r<&L;X7eO-L7dBbEZ;R>N{5qmTx zKlJkA^= zEiZgFoX5Hu|6IS1Bcfa!EO#Gc<~;4TqAi8O3|`Fax7odx z@rawd;{~Y;^TBG^k*G28dgtA$ftl!;!%P1f^L=R>8@t z^vN*0n@lZ`>G*u9lK1wm4C&SWdwPbU+VEBYySjX_;>0V0UZSSsxgHJIM2BGsVLDJx z^i_=zP4ioJH)34L&rFKzezanIyYgJ6m#S3vZ>#Dp=B#vcWO?Y0GHuJ){9dDzuC13#nBCwD`_Ti+@!WcUc_ibs1eP7j|Gb1*lLuCf0d>#*G}OOX_qpw_JAD2s@p$SO2ra zQy%`B?8lANRS^kq)Ba1i$*R@Rt-Z%QJQsVrwQe%R)kG6Z?L!Tod6*}$a;CF0`0mcl zk6M6YNWZ{cN#iDkQptBrmr^_#*I%@XF7zm3=OYps!6&c1{KJ#GGc>Cz@(tstVC~CV zrv$G9n_`r_0=W@c9kFFPPu^tDUJj7`P+1?cv$MTTblQ5jPDvt5Z~AU)KXKf} n$0#%Yq;h6in~!qLr$efQbr7uiS+5Vt-wGnYC)k@sNGkac7s4bn diff --git a/tests/visual_tests/images/lines-1-400-400-2.0-cairo-reference.png b/tests/visual_tests/images/lines-1-400-400-2.0-cairo-reference.png index 3c26f266f861dcc34d00d5d23fea2d2253ea9c03..249edc7093a73941b901d2d10185c1dfaaca6234 100644 GIT binary patch delta 3512 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 delta 3456 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 GNB<9Qf+8CL delta 6386 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 diff --git a/tests/visual_tests/images/lines-1-800-800-1.0-cairo-reference.png b/tests/visual_tests/images/lines-1-800-800-1.0-cairo-reference.png index 9af56e9b46ace60f7d9d8ef054d24b7ed4fd56d9..425b01edfcf4660f3f1acd55c906950db3397db9 100644 GIT binary patch delta 12136 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 Hef<9bT>jNy delta 11128 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 A3IG5A diff --git a/tests/visual_tests/images/lines-2-200-200-1.0-cairo-reference.png b/tests/visual_tests/images/lines-2-200-200-1.0-cairo-reference.png index ef832ce86048f01fa5326d74f449fb9f8306d77e..05dbf909a610995902867f857f6bbc7224564784 100644 GIT binary patch delta 1941 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 diff --git a/tests/visual_tests/images/lines-2-200-200-2.0-cairo-reference.png b/tests/visual_tests/images/lines-2-200-200-2.0-cairo-reference.png index d97b38ca2816073d8a03efda61bd79ed6ea66314..81727a68e5120b167465e0f068c8373e2dd6960f 100644 GIT binary patch literal 1149 zcma)+Ycv}M6vtD{BIXF{SlLL6uGOdoT`Gi%h&VE|S~kM4FjI2n0gM zlS)3L(t*7~s;hW4Kcr42$-cC+?tnmmrY2}w}>o7#oABDVUpsr6pjofWrZS z0M5=pp@5ec`1-=Rb3msZb@)VGi}T1iq@k1M>Sg-HAU##NY~;w!d2wGW(YX$>Mw%F73hZ7V&s7Y z*%yWqYR=%2b1iojx$o6duRJGdhQHo(i-D@u=Ay;y|HsG!etU^iIZlsM7MX$CcSz-B zlUk$jv1?`#G-^44eZ|ZmE|?45=q`Rzde)>?2OKw zWC#emcZt#%ytHy>@gybh7f(O-*y`_h`w!mnFrDD!W}plnv?PQjBb!FV^8OHt}`` z!npNB`}INMtmLCWs-%t7>e8FYieoG9G`wH8K%3%VOD;`v=-9d5BzyMGGKaBgIqUN? zlNTQ9z~92@eIs#HawLgS@B^eA_XB; z6`e|bOwha}a(D_u?xH!gOSO12-fa8n@&ax2itW5rt)^g%?!x`N7d>w~zs8`V$ZfRI zLRBlXoj-3~u6Ek`u+V1VI4SW#**!Veg4)>SG(I|8gE1Mhbm)*WdGRkzce51QwOAeD zK&4G4twgLTB;9A_WNgN_ut*8y-E!BQ&%-mMIaFB z0sh`$avggY%8GI}RwUe$OAhgDgbyGPaPT0gs{;xJy1JmR55~rT!GO6r*w_FL2k!3R z;Q^kW;Oh%PK|mxzWF*AJK}rgwrvrro+1XH72&JV^RRy)R(9{GB1~8exX2ZY$OiTce z2YfyV1RxZGSPTFOV-^$yLXjKb?HO^oX??yhoQ>BRNq&G1cM3l-!|5s$1M2%mZ~oAw zNjs+n$8GUchZ3t2O)bP=^TK%20~}qmbX3XFPl6OxCx*m6{??-ryLD~|2`v7gT zpI9~4ZC|Lc*WPw17-%cJAKIXtE0m}GwWb5_s`($=w9P`|k455-s9bs}4OMc+AZ<{R zW9X==!G$&Q|9#9Num00rbY;&fEzW!7DdvjR+D(x*A20uK1_mj2;5yOC!apZvvso@A z)*MXoqZ^)a{LU~(Xj-l-nyq%>+iBhBX`)+|)a10*Un`b)aeAUs(wwbjt1bR8pm$Ts zY@Wxm27@#z78N~6=SziCIk+2@r`{L`NRmrV8T1r<&L;X7eO-L7dBbEZ;R>N{5qmTx zKlJkA^= zEiZgFoX5Hu|6IS1Bcfa!EO#Gc<~;4TqAi8O3|`Fax7odx z@rawd;{~Y;^TBG^k*G28dgtA$ftl!;!%P1f^L=R>8@t z^vN*0n@lZ`>G*u9lK1wm4C&SWdwPbU+VEBYySjX_;>0V0UZSSsxgHJIM2BGsVLDJx z^i_=zP4ioJH)34L&rFKzezanIyYgJ6m#S3vZ>#Dp=B#vcWO?Y0GHuJ){9dDzuC13#nBCwD`_Ti+@!WcUc_ibs1eP7j|Gb1*lLuCf0d>#*G}OOX_qpw_JAD2s@p$SO2ra zQy%`B?8lANRS^kq)Ba1i$*R@Rt-Z%QJQsVrwQe%R)kG6Z?L!Tod6*}$a;CF0`0mcl zk6M6YNWZ{cN#iDkQptBrmr^_#*I%@XF7zm3=OYps!6&c1{KJ#GGc>Cz@(tstVC~CV zrv$G9n_`r_0=W@c9kFFPPu^tDUJj7`P+1?cv$MTTblQ5jPDvt5Z~AU)KXKf} n$0#%Yq;h6in~!qLr$efQbr7uiS+5Vt-wGnYC)k@sNGkac7s4bn diff --git a/tests/visual_tests/images/lines-2-400-400-2.0-cairo-reference.png b/tests/visual_tests/images/lines-2-400-400-2.0-cairo-reference.png index 62ebc42e2ad53ab92c4f30e9b564bd372407cb34..ca17344c8662f546409f8d5ad4d165df9b2a299d 100644 GIT binary patch delta 4019 zcmV;k4@~gRC$1;3H30$&3X?YhC4V(F|2jJVJw5+KME^`o|4~u@RaO67T$GfQ|6yVO zWo4V2oBwTX|8a5uc6R@IdZ(wS|9^k~gM zv;Vrf|HQ=q%*_AL(Er!h|J>aF;o<-1=l|;J|NsAlXA$)P01?GWL_t(|oKNkYiCfgh zAI8I>+={FSqM}^8>Y{>0Jb#L!fc0!W0$RMSl}c5#MXS=HCeLv6B zx;r~Nlbug8`Oah#lr;i{688p>LX&XE+A60vGb1$F-3As#khC z63rW$6YaZ_#```+Y4lvkcgJ#6LV7nS87emPX(@h$q}toJd;QM0O;kd-9U|HW3L)t`|18YkgI!yS;%z+!K+It%|a~FZ<3oi%^IgA`&7_AnnCz z-+z*jYggi^RU;xHb&AqioMs5=N;IyoPjuy_?-VJuO>MpA+hkjtIFIiViFG|~ZHiLe zrcrQeH?kC$)Z5Y0ShK#NWqYcyq?U%7rZ)X>?QLzUscXrvySIHqo%l*-TQ8j)9y&a7 zQB_9$re3nS2j>nnjA~ol+C-o ztE41lU61HTD~#9KV>h+gOi>#3-C?4C==A8R-KT7XoZj!L9lR5fkY+_`NEM}N?UN8O zaH2(=@s%A$Vy}2z(cGF429-oM+a~S5=5<{gMAuijL1VLHNsUTnQ=+xCS%h^J-GA9^ zElOo`LQ7kp?9jf|Qlp7BzLC-*^Ty4sE!E~3Z}^8BTKcvBppYHfPwh+=q>1*aQ1l^* zws+U)X{jbfsqfKVb!3`W&(^4=rBae}h*e2yVuw+kc1$$pvNdbj`h@R9MNPN)4fAl@ ztSEK5x}?|vHi&-*k7jBZ8Mu7pz<s;Xy?T{?2$$ie{-KfUW}cx5b!=$h=+EMVE5`>99RAT$%S9waYiQ9NO`CW0X4CpKNycH5s#O%d zDLNIU%8bU^MboCtY_%DcQXwTJ<*~s`(y}!f*>;MTmAZH}xlU2^DtmH~V1G*Vo1&jP zvTx$1kub7<_nGk%>GQ%z|DN%42M2x@Ne54lojtm5Y;3$Z&)CVa^P>ZY^`z19!~Lg+ z$IcBL5k3wKk4+rw_jKBcYG9bmcIkEPxv7&(XQ8&5=a}YPkDe0U&rUPjj!ZFfA;l%- zAuMk#%9awHR;Jf$G3rqhd4J#BsegQFFuniqs5lzbk@ST4{}2CD)x(3L8@tj!B9g|` zf-WnUM`t2^*+{yU*1mE!eN8>OTk8Uc_qu8^lgOq{)UK8)Q^`7`RYdz#X3i_3OKa2q ztu{yT8k>z1wAPH-zD_Enq@>IjlF4M3c{&o=sxsLcObY@Lx#=-mjemKh=wGYQySeLQ zCw@rp6Gy;Ezm|IRuzFLd5bQZ58e2Ph@`Ysb2nS}yq50Q+2^6nA{C<3qs5(ds#-WO>@<3ip1dYgcNu3}k*y({CKXa#lB#ys z=7MD1Y(ciB+@WX+=}`))$YdQ&U&uA~jr3{l)!yB9LUtb$FMlqV2)U6yhwB>q#c z?>}KCoyqO)xjg;aQ8Zd2L>)eK(`#duLrAJB+ecU&A-VS6X~dpBg(Pxk?Jl=RNbcw~ z7(RXB+SRlB4u9o0_PO-PwX1vkFPcf?>9ZFu8M$+L4jsC3V|=hw$VB@17^y5a`9SUr8^;8v!hESA`;Tnk{v$xi9K1XZX|UQZKHc1tN_L9Q ztJ>nyy9m+Nw{~=N6m%|DC5enxTl-Y4pn7*g)Go0%G;HrprTTWYH_V!`ha6rP;K%+cs8L6ed}hbZ?7M-L{7!X-jdojf!G!#-nYyy6 zx3Xem$30I!Ffvx$3h~arf4}_WkH7wPk@2rTUjDrryuuYf<%r5%ZDfpaDJd8f0tSVE zK@=fiPzV?l0*1Aa*|UiTnkx!O3J}Aw?asKPzV?l0tSVEK_OsR3W+-${9cpM0!5Qh0x^HXwU7_Q zf%kJyrqn;pH?OCkz4uj$Yazs*&vSRR9sHH~c1Bh2%&&%2HidZQ>)VHBk27r~eAeIG zdL53EXoY-^wDcHQ{qQ939Pm+3n>F7}&8>t`Lxe(>=BB4Id(GmDFa8So5+1?#;KgTC z1t;ZMEpd4=`+joP=da8oDj|PnH=8+c{i>=@7X1_xVob9T>=%8SyHi}EzQ4^CB+Xd; zNp3GhA|%(%{$?b6)ZaY*$8$+2LYC(rN{akCRg~sy$IB7Ekky5!xr10;*3+Jv?;d$= z6bvDsW+ttRoLO6&FHH@kBy1tY-Rv7JaVhuy?mJJ#VFMLy><~i!k(c@Qt>gnwzcQcjgsfP1vr7eWh|Vqd@ganeU*%;!zAOF6 z>cQ?V2usLESvUKtI1bTp9kL?``RK2@Nq>h(NJ6Z;QjO`gua>IZ#fJz&o;nw8iRDz4 zBOwWqb)|yE*OCw11S5X{LO%Je?>Ar7g(O7Ym8!o>4<7ZcW4QoAe)nDBvY+b05%R&` z7T%GPhgv>f`nD17KS;+fkKaBMX+YFpJj|!$bx-=kKe7VE!szzPd(QeSV*8iU3Hp1iC4%Y zo6_#jn+qB~&Deic>UkkbU?G17Y^F!HrN=77CY*nkA3uGx2{S!kIt(gAKJT7Ea-AfL z?b@wah5RNhs<&v8rvAZ6t4JbrD?_NAeU63A2K8Ca9KtAtoHLWfW06Z>b3?{^8-o4~pEe*$R zg|(7DR957{h-Gvj?UkQ4-mpLt$32Kyf z7Ip*?ViTxa#r&(%UfFr|Neq-O?=!KE(^sW|ueTL*L4?TX-L2vsLwm2o*|M-KZDWi5O+FJ*&T)u*;>2KgOtu+T(>?RjjlK;z zd18MPr#~!;T5P6}yxx@?Oo)6qTe^zU=2Di|De7`zv+zCdmbK;AHdhxs&q4&8?e#F6 zmES_*W3fzmuwAok+?coePClQf;rZG^Kp`H6v+~`@amjUT*k9|Mnp>Q8SmI&7V!(=k zLgd5Q;#=0_5!s}#8(Ydkz$H#`tjn^wGvt49ZfkOncCf%>pk7XV-_B;@d`D6VkuGw$ zuaBS#AMH>IRh9D%st@V!c?(qxpy}!lxc%}fuS;s&fK#1Sz6Q7UB4db?G$2;+) zI0otfLVVtZ?|1cI7(#sCMCBZ)0|@`4VnDcXeQ+39)%_L-x@Q!3gzH<8I@_Dc9XBo{+V*C5vBq zE5K>IkdPn7?;<{&oLO)`FO4Ws=q;16b1W@ z&yj^hLv(+oEiXRw-_24;m>5xTp!l3Z!sW~JU=ur{qYjU7ep1x%Pp^WL;S$%AU=tx0 Z{|9W&(KnX*;eG%B002ovPDHLkV1iu=3CREe delta 4050 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 Ef^lLS@&Et; diff --git a/tests/visual_tests/images/lines-2-600-600-1.0-cairo-reference.png b/tests/visual_tests/images/lines-2-600-600-1.0-cairo-reference.png index bf2eeda13423038ea9ea82a6eedd91135f2c9d68..d7f2277cbc4820a9245fef6b1b2c58acec1a8162 100644 GIT binary patch delta 6948 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 delta 5177 zcmXX~c{tQ-*zV+X#6(mm5)#g_q{yD;L@I01!5GUlvLsuwkL6d{%C51T!I)FRiNP=! z%OGnp#@IF1?8{i1k!3LR`<(Cl-aqgAe&6T5?(2Hq=enMDb*_5qm0yZ0-2q%o$Kj3=|g!FI@tqr9nA4@Q**ht5?D6*B7BsP(x$=&p$yuJ@D2o(9jSx zG6GFaK`SfJ+8TtzK}W~*^mNeGH76$reD)0V@d5q(z@Q)yg(@m40;8kB`1tDTYA`Jg z%*+Ha7%)E{#N#OxfC3VUU|k*9-VXNkfc^d8@Gv+v1uiXtYil3~&U=fsA3JvbwDFzW ze}&|(Pre1tx*m^XrhDp)y{<`Dy!;RjubR00y#n?tq$tNEG-o;$Je?FPh2(` z1Vi1`>ayptq3W3FoB&ohR4SMfkZp!8>LUBg5s#g8f&bcg?7jlJ|NJC6^>`6riCoC5 zsi?yN+=5|BUrV0G*%61Dq*J~d(}YMv@cT~x_h~(KOy0;<%X>M-#xPlix{)Izc>mFC zw1%eko(YRGl4f;gr0)gv(=cO1c?$&Eap#oqW^U#z$Y|Pt@W`@ad9m%`?&k{0sLANz z-;NLprl%NN0G#l&iypQDR!&mvTg35sHR-MOKm3$7tQ94ZrBs(1n+K3q@zqVc>N4P8 z=Y37z?(8XZ*%%<%XKcQ-+;>;LOmeE~542rH7wM_Ut-lNQGw*8dZsVPXCwIHQ&DyUr zoF*Io09iU}eGMpY#6m>MsWJqEbKlR7CaD*`{I#9#73ADJBX$7%G~-Dzus1e`<&+e$ zkxAQPcjI|FKFR8LRLm8!yz(R1#^a_u6f@s>l}1>0@bE*bimQ!a*e1=fWi+Z|{K7cw z?{_O*?h&Zn_j3wU(Z=^fP7`nN1sG z*ZMXy{$5d~?f@Ua>DUZIMk8s+l@TE!>-$N8h$jJ$A4h4OS|78_E>$IZ88$<`hnL;J zqJ5i#mfxV)7k!0S=-Pdx4}`lRdr1l0pOEO`8l*Q9S6P~$P#8+{x0P=;rU2D;Se)ia zP8VKcr+Li5>1x!y83le-Ktdf; z&HnXXW9n!a2z)P{)i+y%0s+(SPamp6b?bdWJ)8?sVft2Zz;u6910rl4`#2BnE4HKq zc#TdOp2dkR>ICTOg+2xCaiPZ(-og`lX)*k6YWP95->O;6uamlF4%H{`c=(8rhgPm-c8O9u(1PEF}(+4i@o|h*221qoy~a|^bbL><~_Mn zeX+6B`^Zp1i25R-*!Rp!l z^^{@nbl=nU#~O;ZRXK~w+O@$TY!Q~<6gS7uX%ttZC2b>Y$)V{mU%-=Qj;b#kvvgpDEQxZJm^T}c*?pssYaS%k)Me;vvy0_A|`oK^(*O_s5 z!3_?Xz~c5IHy4(o!Qlp8ezannVlSHWoc2CAg)<&z1d*2v2WZV0X3EvX&EE0B&rb@` z9|){i4wDN9OS%O<+D8Q!&cqkpYS4$3?6@(mnybfJN0`#rk~J2R&aw@}s3Z1~9?H=g zxpw#xZN^)s`ohzz$x^$`s0dpatYK=WNgHu?q=UJ;E~QXc6_Wuln4^h|1wy{shxgwP z5;Xh9<23$DqHMg%^&nYNuLd5oFd00`;-6BoAuL9QA37M_h$DlQxXlRCwG|GF@*p&C zl3OzNHk(ixH%%T|jw(Pm5jr!orovXq&KV7K(M0x>*kOc|o>XN-hhj7QH$f?={|p)( zHib`IjAFip;K)q}UdVw@OtgFN3ID96&@F(PqME1sP-0|-8QhCF3}8i1&RxlDB2?