diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c2601731..23dd2a522 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -341,7 +341,7 @@ SET(CMake_VERSION_MAJOR 2) SET(CMake_VERSION_MINOR 8) SET(CMake_VERSION_PATCH 2) SET(CMake_VERSION_TWEAK 0) -SET(CMake_VERSION_RC 3) +#SET(CMake_VERSION_RC 4) # Releases define a tweak level. IF(DEFINED CMake_VERSION_TWEAK) diff --git a/ChangeLog.manual b/ChangeLog.manual index 333d005bf..7b0a90135 100644 --- a/ChangeLog.manual +++ b/ChangeLog.manual @@ -1,3 +1,28 @@ +No changes in CMake 2.8.2 since 2.8.2-rc4. + +Changes in CMake 2.8.2-rc4 (since 2.8.2-rc3) +-------------------------------------------- +Bill Hoffman (1): + Fix for bug #10859, ctest exit exception incorrectly reported. + +Brad King (3): + Run CMake.HTML test without net access (#10857) + Run CMake.HTML test with older xmllint (#10857) + CTest: Parse empty Git commits correctly + +David Cole (2): + Qualify name of extraction location with ExternalProject name. + For VS10: Really use full path file names. + +James Bigler (1): + Add support for the emulation version of the cudart library. + +Mathieu Malaterre (1): + Cleanup FindOpenSSL. Add support for win64 installation. + +Zach Mullen (1): + Parallel CTest hangs if serial test has depends + Changes in CMake 2.8.2-rc3 (since 2.8.2-rc2) -------------------------------------------- Brad King (1): diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake index d0845e6c8..8c249dc7f 100644 --- a/Modules/ExternalProject.cmake +++ b/Modules/ExternalProject.cmake @@ -358,7 +358,7 @@ endif() endfunction(_ep_write_verifyfile_script) -function(_ep_write_extractfile_script script_filename filename directory) +function(_ep_write_extractfile_script script_filename name filename directory) set(args "") if(filename MATCHES "(\\.bz2|\\.tar\\.gz|\\.tgz|\\.zip)$") @@ -391,10 +391,10 @@ endif() # Prepare a space for extracting: # set(i 1234) -while(EXISTS \"\${directory}/../ex\${i}\") +while(EXISTS \"\${directory}/../ex-${name}\${i}\") math(EXPR i \"\${i} + 1\") endwhile() -set(ut_dir \"\${directory}/../ex\${i}\") +set(ut_dir \"\${directory}/../ex-${name}\${i}\") file(MAKE_DIRECTORY \"\${ut_dir}\") # Extract it: @@ -895,8 +895,7 @@ function(_ep_add_download_command name) endif() _ep_write_verifyfile_script("${stamp_dir}/verify-${name}.cmake" "${file}" "${md5}") list(APPEND cmd ${CMAKE_COMMAND} -P ${stamp_dir}/verify-${name}.cmake) - # TODO: Support other archive formats. - _ep_write_extractfile_script("${stamp_dir}/extract-${name}.cmake" "${file}" "${source_dir}") + _ep_write_extractfile_script("${stamp_dir}/extract-${name}.cmake" "${name}" "${file}" "${source_dir}") list(APPEND cmd ${CMAKE_COMMAND} -P ${stamp_dir}/extract-${name}.cmake) endif() else() diff --git a/Modules/FindCUDA.cmake b/Modules/FindCUDA.cmake index 17da9e926..7099609f8 100644 --- a/Modules/FindCUDA.cmake +++ b/Modules/FindCUDA.cmake @@ -418,6 +418,10 @@ if(NOT "${CUDA_TOOLKIT_ROOT_DIR}" STREQUAL "${CUDA_TOOLKIT_ROOT_DIR_INTERNAL}") unset(CUDA_VERSION CACHE) unset(CUDA_TOOLKIT_INCLUDE CACHE) unset(CUDA_CUDART_LIBRARY CACHE) + if(CUDA_VERSION VERSION_EQUAL "3.0") + # This only existed in the 3.0 version of the CUDA toolkit + unset(CUDA_CUDARTEMU_LIBRARY CACHE) + endif() unset(CUDA_CUDA_LIBRARY CACHE) unset(CUDA_cublas_LIBRARY CACHE) unset(CUDA_cublasemu_LIBRARY CACHE) @@ -549,11 +553,28 @@ endmacro() # CUDA_LIBRARIES find_library_local_first(CUDA_CUDART_LIBRARY cudart "\"cudart\" library") -set(CUDA_LIBRARIES ${CUDA_CUDART_LIBRARY}) +if(CUDA_VERSION VERSION_EQUAL "3.0") + # The cudartemu library only existed for the 3.0 version of CUDA. + find_library_local_first(CUDA_CUDARTEMU_LIBRARY cudartemu "\"cudartemu\" library") + mark_as_advanced( + CUDA_CUDARTEMU_LIBRARY + ) +endif() +# If we are using emulation mode and we found the cudartemu library then use +# that one instead of cudart. +if(CUDA_BUILD_EMULATION AND CUDA_CUDARTEMU_LIBRARY) + set(CUDA_LIBRARIES ${CUDA_CUDARTEMU_LIBRARY}) +else() + set(CUDA_LIBRARIES ${CUDA_CUDART_LIBRARY}) +endif() if(APPLE) # We need to add the path to cudart to the linker using rpath, since the # library name for the cuda libraries is prepended with @rpath. - get_filename_component(_cuda_path_to_cudart "${CUDA_CUDART_LIBRARY}" PATH) + if(CUDA_BUILD_EMULATION AND CUDA_CUDARTEMU_LIBRARY) + get_filename_component(_cuda_path_to_cudart "${CUDA_CUDARTEMU_LIBRARY}" PATH) + else() + get_filename_component(_cuda_path_to_cudart "${CUDA_CUDART_LIBRARY}" PATH) + endif() if(_cuda_path_to_cudart) list(APPEND CUDA_LIBRARIES -Wl,-rpath "-Wl,${_cuda_path_to_cudart}") endif() diff --git a/Modules/FindOpenSSL.cmake b/Modules/FindOpenSSL.cmake index b05602091..826ae096e 100644 --- a/Modules/FindOpenSSL.cmake +++ b/Modules/FindOpenSSL.cmake @@ -1,6 +1,9 @@ # - Try to find the OpenSSL encryption library # Once done this will define # +# OPENSSL_ROOT_DIR - Set this variable to the root installation of OpenSSL +# +# Read-Only variables: # OPENSSL_FOUND - system has the OpenSSL library # OPENSSL_INCLUDE_DIR - the OpenSSL include directory # OPENSSL_LIBRARIES - The libraries needed to use OpenSSL @@ -8,7 +11,7 @@ #============================================================================= # Copyright 2006-2009 Kitware, Inc. # Copyright 2006 Alexander Neundorf -# Copyright 2009 Mathieu Malaterre +# Copyright 2009-2010 Mathieu Malaterre # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. @@ -21,8 +24,23 @@ # License text for the above reference.) # http://www.slproweb.com/products/Win32OpenSSL.html +SET(_OPENSSL_ROOT_HINTS + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]" + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;Inno Setup: App Path]" + ) +SET(_OPENSSL_ROOT_PATHS + "C:/OpenSSL/" + ) +FIND_PATH(OPENSSL_ROOT_DIR + NAMES include/openssl/ssl.h + HINTS ${_OPENSSL_ROOT_HINTS} + PATHS ${_OPENSSL_ROOT_PATHS} +) +MARK_AS_ADVANCED(OPENSSL_ROOT_DIR) + +# Re-use the previous path: FIND_PATH(OPENSSL_INCLUDE_DIR openssl/ssl.h - PATHS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]/include" + ${OPENSSL_ROOT_DIR}/include ) IF(WIN32 AND NOT CYGWIN) @@ -43,16 +61,16 @@ IF(WIN32 AND NOT CYGWIN) # libeay32MD.lib is identical to ../libeay32.lib, and # ssleay32MD.lib is identical to ../ssleay32.lib FIND_LIBRARY(LIB_EAY_DEBUG NAMES libeay32MDd libeay32 - PATHS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]/lib/VC" + ${OPENSSL_ROOT_DIR}/lib/VC ) FIND_LIBRARY(LIB_EAY_RELEASE NAMES libeay32MD libeay32 - PATHS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]/lib/VC" + ${OPENSSL_ROOT_DIR}/lib/VC ) FIND_LIBRARY(SSL_EAY_DEBUG NAMES ssleay32MDd ssleay32 ssl - PATHS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]/lib/VC" + ${OPENSSL_ROOT_DIR}/lib/VC ) FIND_LIBRARY(SSL_EAY_RELEASE NAMES ssleay32MD ssleay32 ssl - PATHS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]/lib/VC" + ${OPENSSL_ROOT_DIR}/lib/VC ) if( CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE ) set( OPENSSL_LIBRARIES @@ -67,20 +85,20 @@ IF(WIN32 AND NOT CYGWIN) ELSEIF(MINGW) # same player, for MingW FIND_LIBRARY(LIB_EAY NAMES libeay32 - PATHS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]/lib/MinGW" + ${OPENSSL_ROOT_DIR}/lib/MinGW ) FIND_LIBRARY(SSL_EAY NAMES ssleay32 - PATHS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]/lib/MinGW" + ${OPENSSL_ROOT_DIR}/lib/MinGW ) MARK_AS_ADVANCED(SSL_EAY LIB_EAY) set( OPENSSL_LIBRARIES ${SSL_EAY} ${LIB_EAY} ) ELSE(MSVC) # Not sure what to pick for -say- intel, let's use the toplevel ones and hope someone report issues: FIND_LIBRARY(LIB_EAY NAMES libeay32 - PATHS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]/lib" + ${OPENSSL_ROOT_DIR}/lib ) FIND_LIBRARY(SSL_EAY NAMES ssleay32 - PATHS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]/lib" + ${OPENSSL_ROOT_DIR}/lib ) MARK_AS_ADVANCED(SSL_EAY LIB_EAY) set( OPENSSL_LIBRARIES ${SSL_EAY} ${LIB_EAY} ) diff --git a/Source/CTest/cmCTestGIT.cxx b/Source/CTest/cmCTestGIT.cxx index 6c3631c90..a49c8521a 100644 --- a/Source/CTest/cmCTestGIT.cxx +++ b/Source/CTest/cmCTestGIT.cxx @@ -317,8 +317,12 @@ protected: \n Log message indented by (4) spaces\n (even blank lines have the spaces)\n + [[ \n [Diff format] + OR + \0 + ]] The header may have more fields. See 'git help diff-tree'. */ @@ -372,6 +376,11 @@ private: { if(this->Line.empty()) { + if(this->Section == SectionBody && this->LineEnd == '\0') + { + // Skip SectionDiff + this->NextSection(); + } this->NextSection(); } else diff --git a/Source/CTest/cmCTestMultiProcessHandler.cxx b/Source/CTest/cmCTestMultiProcessHandler.cxx index 9b8cef5cc..1eb84e85b 100644 --- a/Source/CTest/cmCTestMultiProcessHandler.cxx +++ b/Source/CTest/cmCTestMultiProcessHandler.cxx @@ -92,6 +92,7 @@ void cmCTestMultiProcessHandler::StartTestProcess(int test) this->TestRunningMap[test] = true; // mark the test as running // now remove the test itself this->EraseTest(test); + this->RunningCount += GetProcessorsUsed(test); cmCTestRunTest* testRun = new cmCTestRunTest(this->TestHandler); testRun->SetIndex(test); @@ -267,7 +268,6 @@ void cmCTestMultiProcessHandler::StartNextTests() return; } numToStart -= processors; - this->RunningCount += processors; } else { diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx index 9fb8827e9..3719d45f2 100644 --- a/Source/CTest/cmCTestRunTest.cxx +++ b/Source/CTest/cmCTestRunTest.cxx @@ -221,7 +221,7 @@ bool cmCTestRunTest::EndTest(size_t completed, size_t total, bool started) { outputTestErrorsToConsole = this->CTest->OutputTestOutputOnTestFailure; cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Exception: "); - switch ( retVal ) + switch(this->TestProcess->GetExitException()) { case cmsysProcess_Exception_Fault: cmCTestLog(this->CTest, HANDLER_OUTPUT, "SegFault"); diff --git a/Source/CTest/cmProcess.cxx b/Source/CTest/cmProcess.cxx index 9aa40d6f9..0ee631fe9 100644 --- a/Source/CTest/cmProcess.cxx +++ b/Source/CTest/cmProcess.cxx @@ -264,3 +264,9 @@ int cmProcess::ReportStatus() return result; } + + +int cmProcess::GetExitException() +{ + return cmsysProcess_GetExitException(this->Process); +} diff --git a/Source/CTest/cmProcess.h b/Source/CTest/cmProcess.h index 01dacf925..ff99ca23d 100644 --- a/Source/CTest/cmProcess.h +++ b/Source/CTest/cmProcess.h @@ -43,7 +43,7 @@ public: void SetId(int id) { this->Id = id;} int GetExitValue() { return this->ExitValue;} double GetTotalTime() { return this->TotalTime;} - + int GetExitException(); /** * Read one line of output but block for no more than timeout. * Returns: diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index e3f195a05..e411d3e54 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx @@ -234,7 +234,9 @@ void cmLocalVisualStudio7Generator //---------------------------------------------------------------------------- cmSourceFile* cmLocalVisualStudio7Generator::CreateVCProjBuildRule() { - std::string stampName = cmake::GetCMakeFilesDirectoryPostSlash(); + std::string stampName = this->Makefile->GetCurrentOutputDirectory(); + stampName += "/"; + stampName += cmake::GetCMakeFilesDirectoryPostSlash(); stampName += "generate.stamp"; const char* dsprule = this->Makefile->GetRequiredDefinition("CMAKE_COMMAND"); diff --git a/Source/cmProcessTools.cxx b/Source/cmProcessTools.cxx index cacd7663a..d2f7bf320 100644 --- a/Source/cmProcessTools.cxx +++ b/Source/cmProcessTools.cxx @@ -44,7 +44,7 @@ void cmProcessTools::RunProcess(struct cmsysProcess_s* cp, //---------------------------------------------------------------------------- cmProcessTools::LineParser::LineParser(char sep, bool ignoreCR): - Separator(sep), IgnoreCR(ignoreCR), Log(0), Prefix(0) + Separator(sep), IgnoreCR(ignoreCR), Log(0), Prefix(0), LineEnd('\0') { } @@ -61,8 +61,10 @@ bool cmProcessTools::LineParser::ProcessChunk(const char* first, int length) const char* last = first + length; for(const char* c = first; c != last; ++c) { - if(*c == this->Separator) + if(*c == this->Separator || *c == '\0') { + this->LineEnd = *c; + // Log this line. if(this->Log && this->Prefix) { diff --git a/Source/cmProcessTools.h b/Source/cmProcessTools.h index 0b210af10..439726dd0 100644 --- a/Source/cmProcessTools.h +++ b/Source/cmProcessTools.h @@ -55,6 +55,7 @@ public: bool IgnoreCR; std::ostream* Log; const char* Prefix; + char LineEnd; std::string Line; virtual bool ProcessChunk(const char* data, int length); diff --git a/Source/kwsys/kwsysDateStamp.cmake b/Source/kwsys/kwsysDateStamp.cmake index 200b4f838..043f0d0b0 100644 --- a/Source/kwsys/kwsysDateStamp.cmake +++ b/Source/kwsys/kwsysDateStamp.cmake @@ -18,4 +18,4 @@ SET(KWSYS_DATE_STAMP_YEAR 2010) SET(KWSYS_DATE_STAMP_MONTH 06) # KWSys version date day component. Format is DD. -SET(KWSYS_DATE_STAMP_DAY 22) +SET(KWSYS_DATE_STAMP_DAY 28) diff --git a/Tests/CTestTestParallel/CMakeLists.txt b/Tests/CTestTestParallel/CMakeLists.txt index 8fab44b45..fc53f6874 100644 --- a/Tests/CTestTestParallel/CMakeLists.txt +++ b/Tests/CTestTestParallel/CMakeLists.txt @@ -11,3 +11,5 @@ SET_TESTS_PROPERTIES(TestRunSerial1 TestRunSerial2 PROPERTIES RUN_SERIAL true) ADD_TEST (TestProcessorsGreaterThanMPL1 LockFile) ADD_TEST (TestProcessorsGreaterThanMPL2 LockFile) SET_TESTS_PROPERTIES(TestProcessorsGreaterThanMPL1 PROPERTIES PROCESSORS 10) +SET_TESTS_PROPERTIES(TestProcessorsGreaterThanMPL1 PROPERTIES DEPENDS + TestProcessorsGreaterThanMPL2) diff --git a/Tests/CTestUpdateGIT.cmake.in b/Tests/CTestUpdateGIT.cmake.in index 4ac1b3177..f672a524e 100644 --- a/Tests/CTestUpdateGIT.cmake.in +++ b/Tests/CTestUpdateGIT.cmake.in @@ -131,6 +131,22 @@ run_child( COMMAND ${GIT} submodule update ) +# Save the first revision name. +execute_process( + WORKING_DIRECTORY ${TOP}/user-source + COMMAND ${GIT} rev-parse HEAD + OUTPUT_VARIABLE revision1 + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + +#----------------------------------------------------------------------------- +# Create an empty commit. +message("Creating empty commit...") +run_child( + WORKING_DIRECTORY ${TOP}/user-source + COMMAND ${GIT} commit --allow-empty -m "Empty commit" + ) + #----------------------------------------------------------------------------- # Make changes in the working tree. message("Changing content...") @@ -196,7 +212,7 @@ macro(rewind_source src_dir) message("Backing up to revision 1...") run_child( WORKING_DIRECTORY ${TOP}/${src_dir} - COMMAND ${GIT} reset --hard origin/master~2 + COMMAND ${GIT} reset --hard ${revision1} ) run_child( WORKING_DIRECTORY ${TOP}/${src_dir} @@ -261,7 +277,7 @@ execute_process( ) execute_process( WORKING_DIRECTORY \"${TOP}/dash-source\" - COMMAND \"${GIT}\" reset --hard master~2 + COMMAND \"${GIT}\" reset --hard ${revision1} ) execute_process( WORKING_DIRECTORY \"${TOP}/dash-source\" diff --git a/Utilities/CMakeLists.txt b/Utilities/CMakeLists.txt index ecae7b0c9..4c9202c47 100644 --- a/Utilities/CMakeLists.txt +++ b/Utilities/CMakeLists.txt @@ -138,8 +138,24 @@ if(BUILD_TESTING) endif() mark_as_advanced(LIBXML2_XMLLINT_EXECUTABLE) if(LIBXML2_XMLLINT_EXECUTABLE) - add_test(CMake.HTML - ${LIBXML2_XMLLINT_EXECUTABLE} --valid --noout ${HTML_FILES} - ) + execute_process(COMMAND ${LIBXML2_XMLLINT_EXECUTABLE} --help + OUTPUT_VARIABLE _help ERROR_VARIABLE _err) + if("${_help}" MATCHES "--path" AND "${_help}" MATCHES "--nonet") + # We provide the XHTML DTD and its dependencies in the 'xml' + # directory so that xmllint can run without network access. + # However, it's --path option accepts a space-separated list of + # paths so it cannot handle spaces in the path to the source tree. + # Therefore we run the tool with the current work directory set to + # the 'xml' directory and use '.' as the path. + add_test(CMake.HTML + ${CMAKE_CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_SOURCE_DIR}/xml + ${LIBXML2_XMLLINT_EXECUTABLE} --valid --noout --nonet --path . + ${HTML_FILES} + ) + else() + add_test(CMake.HTML + ${LIBXML2_XMLLINT_EXECUTABLE} --valid --noout ${HTML_FILES} + ) + endif() endif() endif() diff --git a/Utilities/xml/xhtml-lat1.ent b/Utilities/xml/xhtml-lat1.ent new file mode 100644 index 000000000..ffee223eb --- /dev/null +++ b/Utilities/xml/xhtml-lat1.ent @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Utilities/xml/xhtml-special.ent b/Utilities/xml/xhtml-special.ent new file mode 100644 index 000000000..3a83fb656 --- /dev/null +++ b/Utilities/xml/xhtml-special.ent @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Utilities/xml/xhtml-symbol.ent b/Utilities/xml/xhtml-symbol.ent new file mode 100644 index 000000000..d0c77eebc --- /dev/null +++ b/Utilities/xml/xhtml-symbol.ent @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Utilities/xml/xhtml1-strict.dtd b/Utilities/xml/xhtml1-strict.dtd new file mode 100644 index 000000000..e48fbea6e --- /dev/null +++ b/Utilities/xml/xhtml1-strict.dtd @@ -0,0 +1,977 @@ + + + + + +%HTMLlat1; + + +%HTMLsymbol; + + +%HTMLspecial; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +