diff options
62 files changed, 775 insertions, 121 deletions
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 344bc09..38cc0d8 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -154,6 +154,7 @@ Properties on Targets /prop_tgt/CROSSCOMPILING_EMULATOR /prop_tgt/CUDA_PTX_COMPILATION /prop_tgt/CUDA_SEPARABLE_COMPILATION + /prop_tgt/CUDA_RESOLVE_DEVICE_SYMBOLS /prop_tgt/CUDA_EXTENSIONS /prop_tgt/CUDA_STANDARD /prop_tgt/CUDA_STANDARD_REQUIRED diff --git a/Help/prop_tgt/CUDA_RESOLVE_DEVICE_SYMBOLS.rst b/Help/prop_tgt/CUDA_RESOLVE_DEVICE_SYMBOLS.rst new file mode 100644 index 0000000..127d79f --- /dev/null +++ b/Help/prop_tgt/CUDA_RESOLVE_DEVICE_SYMBOLS.rst @@ -0,0 +1,15 @@ +CUDA_RESOLVE_DEVICE_SYMBOLS +--------------------------- + +CUDA only: Enables device linking for the specific static library target + +If set this will enable device linking on this static library target. Normally +device linking is deferred until a shared library or executable is generated, +allowing for multiple static libraries to resolve device symbols at the same +time. + +For instance: + +.. code-block:: cmake + + set_property(TARGET mystaticlib PROPERTY CUDA_RESOLVE_DEVICE_SYMBOLS ON) diff --git a/Help/release/dev/ctest_test-ignore-skipped-tests.rst b/Help/release/dev/ctest_test-ignore-skipped-tests.rst new file mode 100644 index 0000000..1e2486c --- /dev/null +++ b/Help/release/dev/ctest_test-ignore-skipped-tests.rst @@ -0,0 +1,7 @@ +ctest_test-ignore-skipped-tests +------------------------------- + +* When running tests, CTest learned to treat skipped tests (using the + :prop_test:`SKIP_RETURN_CODE` property) the same as tests with the + :prop_test:`DISABLED` property. Due to this change, CTest will not indicate + failure when all tests are either skipped or pass. diff --git a/Modules/CPackIFW.cmake b/Modules/CPackIFW.cmake index 099dd1c..b4a6dc6 100644 --- a/Modules/CPackIFW.cmake +++ b/Modules/CPackIFW.cmake @@ -717,21 +717,58 @@ set(CPackIFW_CMake_INCLUDED 1) # Framework version #============================================================================= -if(CPACK_IFW_INSTALLERBASE_EXECUTABLE AND CPACK_IFW_DEVTOOL_EXECUTABLE) - execute_process(COMMAND - "${CPACK_IFW_INSTALLERBASE_EXECUTABLE}" --framework-version - OUTPUT_VARIABLE CPACK_IFW_FRAMEWORK_VERSION) - if(CPACK_IFW_FRAMEWORK_VERSION) - string(REPLACE " " "" - CPACK_IFW_FRAMEWORK_VERSION "${CPACK_IFW_FRAMEWORK_VERSION}") - string(REPLACE "\t" "" - CPACK_IFW_FRAMEWORK_VERSION "${CPACK_IFW_FRAMEWORK_VERSION}") - string(REPLACE "\n" "" - CPACK_IFW_FRAMEWORK_VERSION "${CPACK_IFW_FRAMEWORK_VERSION}") - if(CPACK_IFW_VERBOSE) - message(STATUS "Found QtIFW ${CPACK_IFW_FRAMEWORK_VERSION} version") +set(CPACK_IFW_FRAMEWORK_VERSION_FORCED "" + CACHE STRING "The forced version of used QtIFW tools") +mark_as_advanced(CPACK_IFW_FRAMEWORK_VERSION_FORCED) +set(CPACK_IFW_FRAMEWORK_VERSION_TIMEOUT 1 + CACHE STRING "The timeout to return QtIFW framework version string from \"installerbase\" executable") +mark_as_advanced(CPACK_IFW_FRAMEWORK_VERSION_TIMEOUT) +if(CPACK_IFW_INSTALLERBASE_EXECUTABLE AND NOT CPACK_IFW_FRAMEWORK_VERSION_FORCED) + set(CPACK_IFW_FRAMEWORK_VERSION) + # Invoke version from "installerbase" executable + foreach(_ifw_version_argument --framework-version --version) + if(NOT CPACK_IFW_FRAMEWORK_VERSION) + execute_process(COMMAND + "${CPACK_IFW_INSTALLERBASE_EXECUTABLE}" ${_ifw_version_argument} + TIMEOUT ${CPACK_IFW_FRAMEWORK_VERSION_TIMEOUT} + RESULT_VARIABLE CPACK_IFW_FRAMEWORK_VERSION_RESULT + OUTPUT_VARIABLE CPACK_IFW_FRAMEWORK_VERSION_OUTPUT + OUTPUT_STRIP_TRAILING_WHITESPACE + ENCODING UTF8) + if(NOT CPACK_IFW_FRAMEWORK_VERSION_RESULT AND CPACK_IFW_FRAMEWORK_VERSION_OUTPUT) + string(REGEX MATCH "[0-9]+(\\.[0-9]+)*" + CPACK_IFW_FRAMEWORK_VERSION "${CPACK_IFW_FRAMEWORK_VERSION_OUTPUT}") + if(CPACK_IFW_FRAMEWORK_VERSION) + if("${_ifw_version_argument}" STREQUAL "--framework-version") + set(CPACK_IFW_FRAMEWORK_VERSION_SOURCE "INSTALLERBASE_FRAMEWORK_VERSION") + elseif("${_ifw_version_argument}" STREQUAL "--version") + set(CPACK_IFW_FRAMEWORK_VERSION_SOURCE "INSTALLERBASE_FRAMEWORK_VERSION") + endif() + endif() + endif() + endif() + endforeach() + # Finaly try to get version from executable path + if(NOT CPACK_IFW_FRAMEWORK_VERSION) + string(REGEX MATCH "[0-9]+(\\.[0-9]+)*" + CPACK_IFW_FRAMEWORK_VERSION "${CPACK_IFW_INSTALLERBASE_EXECUTABLE}") + if(CPACK_IFW_FRAMEWORK_VERSION) + set(CPACK_IFW_FRAMEWORK_VERSION_SOURCE "INSTALLERBASE_PATH") endif() endif() +elseif(CPACK_IFW_FRAMEWORK_VERSION_FORCED) + set(CPACK_IFW_FRAMEWORK_VERSION ${CPACK_IFW_FRAMEWORK_VERSION_FORCED}) + set(CPACK_IFW_FRAMEWORK_VERSION_SOURCE "FORCED") +endif() +if(CPACK_IFW_VERBOSE) + if(CPACK_IFW_FRAMEWORK_VERSION AND CPACK_IFW_FRAMEWORK_VERSION_FORCED) + message(STATUS "Found QtIFW ${CPACK_IFW_FRAMEWORK_VERSION} (forced) version") + elseif(CPACK_IFW_FRAMEWORK_VERSION) + message(STATUS "Found QtIFW ${CPACK_IFW_FRAMEWORK_VERSION} version") + endif() +endif() +if(CPACK_IFW_INSTALLERBASE_EXECUTABLE AND NOT CPACK_IFW_FRAMEWORK_VERSION) + message(WARNING "Could not detect QtIFW tools version. Set used version to variable \"CPACK_IFW_FRAMEWORK_VERSION_FORCED\" manualy.") endif() #============================================================================= diff --git a/Modules/FindGLUT.cmake b/Modules/FindGLUT.cmake index 02e6df0..88d4b29 100644 --- a/Modules/FindGLUT.cmake +++ b/Modules/FindGLUT.cmake @@ -120,10 +120,12 @@ if (GLUT_FOUND) # If not, we need some way to figure out what platform we are on. set( GLUT_LIBRARIES ${GLUT_glut_LIBRARY} - ${GLUT_Xmu_LIBRARY} - ${GLUT_Xi_LIBRARY} - ${GLUT_cocoa_LIBRARY} ) + foreach(v GLUT_Xmu_LIBRARY GLUT_Xi_LIBRARY GLUT_cocoa_LIBRARY) + if(${v}) + list(APPEND GLUT_LIBRARIES ${${v}}) + endif() + endforeach() if(NOT TARGET GLUT::GLUT) add_library(GLUT::GLUT UNKNOWN IMPORTED) diff --git a/Modules/UseSWIG.cmake b/Modules/UseSWIG.cmake index 277f4ca..bfe1a6f 100644 --- a/Modules/UseSWIG.cmake +++ b/Modules/UseSWIG.cmake @@ -326,6 +326,9 @@ macro(SWIG_ADD_LIBRARY name) if (APPLE) set_target_properties (${SWIG_MODULE_${name}_REAL_NAME} PROPERTIES SUFFIX ".bundle") endif () + else() + # assume empty prefix because we expect the module to be dynamically loaded + set_target_properties (${SWIG_MODULE_${name}_REAL_NAME} PROPERTIES PREFIX "") endif () endmacro() diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 61e71b2..d18bf2c 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,5 +1,5 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 8) -set(CMake_VERSION_PATCH 20170426) +set(CMake_VERSION_PATCH 20170428) #set(CMake_VERSION_RC 1) diff --git a/Source/CPack/IFW/cmCPackIFWInstaller.cxx b/Source/CPack/IFW/cmCPackIFWInstaller.cxx index 57b47f1..5e5f066 100644 --- a/Source/CPack/IFW/cmCPackIFWInstaller.cxx +++ b/Source/CPack/IFW/cmCPackIFWInstaller.cxx @@ -171,17 +171,17 @@ void cmCPackIFWInstaller::ConfigureFromOptions() // WizardStyle if (const char* option = GetOption("CPACK_IFW_PACKAGE_WIZARD_STYLE")) { - if (WizardStyle.compare("Modern") == 0 && - WizardStyle.compare("Aero") == 0 && WizardStyle.compare("Mac") == 0 && - WizardStyle.compare("Classic") == 0) { + // Setting the user value in any case + WizardStyle = option; + // Check known values + if (WizardStyle != "Modern" && WizardStyle != "Aero" && + WizardStyle != "Mac" && WizardStyle != "Classic") { cmCPackLogger( cmCPackLog::LOG_WARNING, "Option CPACK_IFW_PACKAGE_WIZARD_STYLE has unknown value \"" << option << "\". Expected values are: Modern, Aero, Mac, Classic." << std::endl); } - - WizardStyle = option; } // WizardDefaultWidth diff --git a/Source/CPack/IFW/cmCPackIFWPackage.cxx b/Source/CPack/IFW/cmCPackIFWPackage.cxx index 99e8b9e..eda383f 100644 --- a/Source/CPack/IFW/cmCPackIFWPackage.cxx +++ b/Source/CPack/IFW/cmCPackIFWPackage.cxx @@ -514,11 +514,11 @@ int cmCPackIFWPackage::ConfigureFromPrefix(const std::string& prefix) Default.clear(); } else if (const char* value = GetOption(option)) { std::string lowerValue = cmsys::SystemTools::LowerCase(value); - if (lowerValue.compare("true") == 0) { + if (lowerValue == "true") { Default = "true"; - } else if (lowerValue.compare("false") == 0) { + } else if (lowerValue == "false") { Default = "false"; - } else if (lowerValue.compare("script") == 0) { + } else if (lowerValue == "script") { Default = "script"; } else { Default = value; diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx index a4853b7..fe23075 100644 --- a/Source/CTest/cmCTestRunTest.cxx +++ b/Source/CTest/cmCTestRunTest.cxx @@ -167,6 +167,7 @@ bool cmCTestRunTest::EndTest(size_t completed, size_t total, bool started) std::vector<std::pair<cmsys::RegularExpression, std::string> >::iterator passIt; bool forceFail = false; + bool skipped = false; bool outputTestErrorsToConsole = false; if (!this->TestProperties->RequiredRegularExpressions.empty() && this->FailedDependencies.empty()) { @@ -219,6 +220,7 @@ bool cmCTestRunTest::EndTest(size_t completed, size_t total, bool started) s << "SKIP_RETURN_CODE=" << this->TestProperties->SkipReturnCode; this->TestResult.CompletionStatus = s.str(); cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Skipped "); + skipped = true; } else if ((success && !this->TestProperties->WillFail) || (!success && this->TestProperties->WillFail)) { this->TestResult.Status = cmCTestTestHandler::COMPLETED; @@ -338,7 +340,9 @@ bool cmCTestRunTest::EndTest(size_t completed, size_t total, bool started) compress ? this->CompressedOutput : this->ProcessOutput; this->TestResult.CompressOutput = compress; this->TestResult.ReturnValue = this->TestProcess->GetExitValue(); - this->TestResult.CompletionStatus = "Completed"; + if (!skipped) { + this->TestResult.CompletionStatus = "Completed"; + } this->TestResult.ExecutionTime = this->TestProcess->GetTotalTime(); this->MemCheckPostProcess(); this->ComputeWeightedCost(); @@ -349,7 +353,7 @@ bool cmCTestRunTest::EndTest(size_t completed, size_t total, bool started) this->TestHandler->TestResults.push_back(this->TestResult); } delete this->TestProcess; - return passed; + return passed || skipped; } bool cmCTestRunTest::StartAgain() diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx index 167fecf..d73811b 100644 --- a/Source/CTest/cmCTestTestHandler.cxx +++ b/Source/CTest/cmCTestTestHandler.cxx @@ -17,6 +17,7 @@ #include <string.h> #include <time.h> +#include "cmAlgorithms.h" #include "cmCTest.h" #include "cmCTestBatchTestHandler.h" #include "cmCTestMultiProcessHandler.h" @@ -495,7 +496,8 @@ int cmCTestTestHandler::ProcessHandler() for (SetOfTests::iterator ftit = resultsSet.begin(); ftit != resultsSet.end(); ++ftit) { - if (ftit->CompletionStatus == "Disabled") { + if (cmHasLiteralPrefix(ftit->CompletionStatus, "SKIP_RETURN_CODE=") || + ftit->CompletionStatus == "Disabled") { disabledTests.push_back(*ftit); } } @@ -521,17 +523,22 @@ int cmCTestTestHandler::ProcessHandler() if (!disabledTests.empty()) { cmGeneratedFileStream ofs; cmCTestLog(this->CTest, HANDLER_OUTPUT, std::endl - << "The following tests are disabled and did not run:" - << std::endl); + << "The following tests did not run:" << std::endl); this->StartLogFile("TestsDisabled", ofs); + const char* disabled_reason; for (std::vector<cmCTestTestHandler::cmCTestTestResult>::iterator dtit = disabledTests.begin(); dtit != disabledTests.end(); ++dtit) { ofs << dtit->TestCount << ":" << dtit->Name << std::endl; + if (dtit->CompletionStatus == "Disabled") { + disabled_reason = "Disabled"; + } else { + disabled_reason = "Skipped"; + } cmCTestLog(this->CTest, HANDLER_OUTPUT, "\t" << std::setw(3) << dtit->TestCount << " - " << dtit->Name - << std::endl); + << " (" << disabled_reason << ")" << std::endl); } } @@ -544,6 +551,7 @@ int cmCTestTestHandler::ProcessHandler() for (SetOfTests::iterator ftit = resultsSet.begin(); ftit != resultsSet.end(); ++ftit) { if (ftit->Status != cmCTestTestHandler::COMPLETED && + !cmHasLiteralPrefix(ftit->CompletionStatus, "SKIP_RETURN_CODE=") && ftit->CompletionStatus != "Disabled") { ofs << ftit->TestCount << ":" << ftit->Name << std::endl; cmCTestLog( @@ -1727,7 +1735,7 @@ void cmCTestTestHandler::ExpandTestsToRunInformationForRerunFailed() // bcc crashes if we attempt a normal substring comparison, // hence the following workaround std::string fileNameSubstring = fileName.substr(0, pattern.length()); - if (fileNameSubstring.compare(pattern) != 0) { + if (fileNameSubstring != pattern) { continue; } if (logName == "") { diff --git a/Source/bindexplib.cxx b/Source/bindexplib.cxx index 6026a57..691e3ae 100644 --- a/Source/bindexplib.cxx +++ b/Source/bindexplib.cxx @@ -235,35 +235,29 @@ public: symbol.erase(posAt); } } - // For i386 builds we don't need to remove _ + // For i386 builds we need to remove _ if (this->IsI386 && symbol[0] == '_') { symbol.erase(0, 1); } - /* - Check whether it is "Scalar deleting destructor" and - "Vector deleting destructor" - */ + // Check whether it is "Scalar deleting destructor" and "Vector + // deleting destructor" + // if scalarPrefix and vectorPrefix are not found then print the + // symbol const char* scalarPrefix = "??_G"; const char* vectorPrefix = "??_E"; - // original code had a check for - // symbol.find("real@") == std::string::npos) - // but if this disallows memmber functions with the name real - // if scalarPrefix and vectorPrefix are not found then print - // the symbol if (symbol.compare(0, 4, scalarPrefix) && symbol.compare(0, 4, vectorPrefix)) { SectChar = this->SectionHeaders[pSymbolTable->SectionNumber - 1] .Characteristics; - if (!pSymbolTable->Type && (SectChar & IMAGE_SCN_MEM_WRITE)) { - // Read only (i.e. constants) must be excluded - this->DataSymbols.insert(symbol); - } else { - if (pSymbolTable->Type || !(SectChar & IMAGE_SCN_MEM_READ) || - (SectChar & IMAGE_SCN_MEM_EXECUTE)) { - this->Symbols.insert(symbol); - } else { - // printf(" strange symbol: %s \n",symbol.c_str()); + + if (SectChar & IMAGE_SCN_MEM_EXECUTE) { + this->Symbols.insert(symbol); + } else if (SectChar & IMAGE_SCN_MEM_READ) { + // skip __real@ and __xmm@ + if (symbol.find("_real") == std::string::npos && + symbol.find("_xmm") == std::string::npos) { + this->DataSymbols.insert(symbol); } } } diff --git a/Source/cmDocumentation.cxx b/Source/cmDocumentation.cxx index 5f25113..c6286b3 100644 --- a/Source/cmDocumentation.cxx +++ b/Source/cmDocumentation.cxx @@ -129,28 +129,19 @@ bool cmDocumentation::PrintRequestedDocumentation(std::ostream& os) this->CurrentArgument = i->Argument; // If a file name was given, use it. Otherwise, default to the // given stream. - cmsys::ofstream* fout = CM_NULLPTR; + cmsys::ofstream fout; std::ostream* s = &os; if (!i->Filename.empty()) { - fout = new cmsys::ofstream(i->Filename.c_str()); - if (fout) { - s = fout; - } else { - result = false; - } + fout.open(i->Filename.c_str()); + s = &fout; } else if (++count > 1) { os << "\n\n"; } // Print this documentation type to the stream. - if (!this->PrintDocumentation(i->HelpType, *s) || !*s) { + if (!this->PrintDocumentation(i->HelpType, *s) || s->fail()) { result = false; } - - // Close the file if we wrote one. - if (fout) { - delete fout; - } } return result; } diff --git a/Source/cmELF.cxx b/Source/cmELF.cxx index 2a8137f..d23abec 100644 --- a/Source/cmELF.cxx +++ b/Source/cmELF.cxx @@ -44,6 +44,9 @@ typedef struct Elf32_Rela Elf32_Rela; #ifdef _SCO_DS #include <link.h> // For DT_SONAME etc. #endif +#ifndef DT_RUNPATH +#define DT_RUNPATH 29 +#endif // Low-level byte swapping implementation. template <size_t s> @@ -154,11 +157,7 @@ public: // Lookup the RUNPATH in the DYNAMIC section. StringEntry const* GetRunPath() { -#if defined(DT_RUNPATH) return this->GetDynamicSectionString(DT_RUNPATH); -#else - return 0; -#endif } // Return the recorded ELF type. diff --git a/Source/cmLinkLineDeviceComputer.cxx b/Source/cmLinkLineDeviceComputer.cxx index 6789555..bf30b39 100644 --- a/Source/cmLinkLineDeviceComputer.cxx +++ b/Source/cmLinkLineDeviceComputer.cxx @@ -39,9 +39,24 @@ std::string cmLinkLineDeviceComputer::ComputeLinkLibraries( continue; } - if (li->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY || - li->Target->GetType() == cmStateEnums::SHARED_LIBRARY || - li->Target->GetType() == cmStateEnums::MODULE_LIBRARY) { + bool skippable = false; + switch (li->Target->GetType()) { + case cmStateEnums::SHARED_LIBRARY: + case cmStateEnums::MODULE_LIBRARY: + case cmStateEnums::INTERFACE_LIBRARY: + skippable = true; + break; + case cmStateEnums::STATIC_LIBRARY: + // If a static library is resolving its device linking, it should + // be removed for other device linking + skippable = + li->Target->GetPropertyAsBool("CUDA_RESOLVE_DEVICE_SYMBOLS"); + break; + default: + break; + } + + if (skippable) { continue; } diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index b8a5293..cb11060 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -3153,7 +3153,7 @@ void cmMakefile::EnableLanguage(std::vector<std::string> const& lang, langs.reserve(lang.size()); for (std::vector<std::string>::const_iterator i = lang.begin(); i != lang.end(); ++i) { - if (i->compare("RC") == 0) { + if (*i == "RC") { langsRC.push_back(*i); } else { langs.push_back(*i); diff --git a/Source/cmMakefileExecutableTargetGenerator.cxx b/Source/cmMakefileExecutableTargetGenerator.cxx index a93b42d..a719887 100644 --- a/Source/cmMakefileExecutableTargetGenerator.cxx +++ b/Source/cmMakefileExecutableTargetGenerator.cxx @@ -122,7 +122,11 @@ void cmMakefileExecutableTargetGenerator::WriteDeviceExecutableRule( std::string buildEcho = "Linking "; buildEcho += linkLanguage; buildEcho += " device code "; - buildEcho += targetOutputReal; + buildEcho += this->LocalGenerator->ConvertToOutputFormat( + this->LocalGenerator->MaybeConvertToRelativePath( + this->LocalGenerator->GetCurrentBinaryDirectory(), + this->DeviceLinkObject), + cmOutputConverter::SHELL); this->LocalGenerator->AppendEcho( commands, buildEcho, cmLocalUnixMakefileGenerator3::EchoLink, &progress); } diff --git a/Source/cmMakefileLibraryTargetGenerator.cxx b/Source/cmMakefileLibraryTargetGenerator.cxx index e017b29..2823977 100644 --- a/Source/cmMakefileLibraryTargetGenerator.cxx +++ b/Source/cmMakefileLibraryTargetGenerator.cxx @@ -127,6 +127,24 @@ void cmMakefileLibraryTargetGenerator::WriteObjectLibraryRules() void cmMakefileLibraryTargetGenerator::WriteStaticLibraryRules() { + const std::string cuda_lang("CUDA"); + cmGeneratorTarget::LinkClosure const* closure = + this->GeneratorTarget->GetLinkClosure(this->ConfigName); + + const bool hasCUDA = + (std::find(closure->Languages.begin(), closure->Languages.end(), + cuda_lang) != closure->Languages.end()); + + const bool resolveDeviceSymbols = + this->GeneratorTarget->GetPropertyAsBool("CUDA_RESOLVE_DEVICE_SYMBOLS"); + if (hasCUDA && resolveDeviceSymbols) { + std::string linkRuleVar = "CMAKE_CUDA_DEVICE_LINK_LIBRARY"; + std::string extraFlags; + this->LocalGenerator->AppendFlags( + extraFlags, this->GeneratorTarget->GetProperty("LINK_FLAGS")); + this->WriteDeviceLibraryRules(linkRuleVar, extraFlags, false); + } + std::string linkLanguage = this->GeneratorTarget->GetLinkerLanguage(this->ConfigName); @@ -292,8 +310,12 @@ void cmMakefileLibraryTargetGenerator::WriteDeviceLibraryRules( cmLocalUnixMakefileGenerator3::EchoProgress progress; this->MakeEchoProgress(progress); // Add the link message. - std::string buildEcho = "Linking " + linkLanguage + " device code"; - buildEcho += targetOutputReal; + std::string buildEcho = "Linking " + linkLanguage + " device code "; + buildEcho += this->LocalGenerator->ConvertToOutputFormat( + this->LocalGenerator->MaybeConvertToRelativePath( + this->LocalGenerator->GetCurrentBinaryDirectory(), + this->DeviceLinkObject), + cmOutputConverter::SHELL); this->LocalGenerator->AppendEcho( commands, buildEcho, cmLocalUnixMakefileGenerator3::EchoLink, &progress); } @@ -857,6 +879,16 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules( std::vector<std::string> object_strings; this->WriteObjectsStrings(object_strings, archiveCommandLimit); + // Add the cuda device object to the list of archive files. This will + // only occur on archives which have CUDA_RESOLVE_DEVICE_SYMBOLS enabled + if (!this->DeviceLinkObject.empty()) { + object_strings.push_back(this->LocalGenerator->ConvertToOutputFormat( + this->LocalGenerator->MaybeConvertToRelativePath( + this->LocalGenerator->GetCurrentBinaryDirectory(), + this->DeviceLinkObject), + cmOutputConverter::SHELL)); + } + // Create the archive with the first set of objects. std::vector<std::string>::iterator osi = object_strings.begin(); { diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index cfc91bd..8206083 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -447,6 +447,7 @@ std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeDeviceLinkCmd() // an executable or a dynamic library. std::string linkCmd; switch (this->GetGeneratorTarget()->GetType()) { + case cmStateEnums::STATIC_LIBRARY: case cmStateEnums::SHARED_LIBRARY: case cmStateEnums::MODULE_LIBRARY: { const std::string cudaLinkCmd( @@ -559,11 +560,15 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement() case cmStateEnums::EXECUTABLE: shouldHaveDeviceLinking = true; break; + case cmStateEnums::STATIC_LIBRARY: + shouldHaveDeviceLinking = + genTarget.GetPropertyAsBool("CUDA_RESOLVE_DEVICE_SYMBOLS"); + break; default: break; } - if (!shouldHaveDeviceLinking || !hasCUDA) { + if (!(shouldHaveDeviceLinking && hasCUDA)) { return; } diff --git a/Source/cmServerProtocol.cxx b/Source/cmServerProtocol.cxx index 8227ab7..11ee897 100644 --- a/Source/cmServerProtocol.cxx +++ b/Source/cmServerProtocol.cxx @@ -50,9 +50,8 @@ std::vector<std::string> getConfigurations(const cmake* cm) bool hasString(const Json::Value& v, const std::string& s) { return !v.isNull() && - std::find_if(v.begin(), v.end(), [s](const Json::Value& i) { - return i.asString() == s; - }) != v.end(); + std::any_of(v.begin(), v.end(), + [s](const Json::Value& i) { return i.asString() == s; }); } template <class T> @@ -493,16 +492,14 @@ cmServerResponse cmServerProtocol1_0::ProcessCache( if (keys.empty()) { keys = allKeys; } else { - for (auto i : keys) { - if (std::find_if(allKeys.begin(), allKeys.end(), - [i](const std::string& j) { return i == j; }) == - allKeys.end()) { + for (const auto& i : keys) { + if (std::find(allKeys.begin(), allKeys.end(), i) == allKeys.end()) { return request.ReportError("Key \"" + i + "\" not found in cache."); } } } std::sort(keys.begin(), keys.end()); - for (auto key : keys) { + for (const auto& key : keys) { Json::Value entry = Json::objectValue; entry[kKEY_KEY] = key; entry[kTYPE_KEY] = @@ -511,7 +508,7 @@ cmServerResponse cmServerProtocol1_0::ProcessCache( Json::Value props = Json::objectValue; bool haveProperties = false; - for (auto prop : state->GetCacheEntryPropertyList(key)) { + for (const auto& prop : state->GetCacheEntryPropertyList(key)) { haveProperties = true; props[prop] = state->GetCacheEntryProperty(key, prop); } @@ -598,7 +595,7 @@ bool LanguageData::operator==(const LanguageData& other) const void LanguageData::SetDefines(const std::set<std::string>& defines) { std::vector<std::string> result; - for (auto i : defines) { + for (const auto& i : defines) { result.push_back(i); } std::sort(result.begin(), result.end()); @@ -615,11 +612,11 @@ struct hash<LanguageData> using std::hash; size_t result = hash<std::string>()(in.Language) ^ hash<std::string>()(in.Flags); - for (auto i : in.IncludePathList) { + for (const auto& i : in.IncludePathList) { result = result ^ (hash<std::string>()(i.first) ^ (i.second ? std::numeric_limits<size_t>::max() : 0)); } - for (auto i : in.Defines) { + for (const auto& i : in.Defines) { result = result ^ hash<std::string>()(i); } result = @@ -643,7 +640,7 @@ static Json::Value DumpSourceFileGroup(const LanguageData& data, } if (!data.IncludePathList.empty()) { Json::Value includes = Json::arrayValue; - for (auto i : data.IncludePathList) { + for (const auto& i : data.IncludePathList) { Json::Value tmp = Json::objectValue; tmp[kPATH_KEY] = i.first; if (i.second) { @@ -661,7 +658,7 @@ static Json::Value DumpSourceFileGroup(const LanguageData& data, result[kIS_GENERATED_KEY] = data.IsGenerated; Json::Value sourcesValue = Json::arrayValue; - for (auto i : files) { + for (const auto& i : files) { const std::string relPath = cmSystemTools::RelativePath(baseDir.c_str(), i.c_str()); sourcesValue.append(relPath.size() < i.size() ? relPath : i); @@ -819,7 +816,7 @@ static Json::Value DumpTarget(cmGeneratorTarget* target, std::set<std::string> languages; target->GetLanguages(languages, config); std::map<std::string, LanguageData> languageDataMap; - for (auto lang : languages) { + for (const auto& lang : languages) { LanguageData& ld = languageDataMap[lang]; ld.Language = lang; lg->GetTargetCompileFlags(target, config, lang, ld.Flags); @@ -1095,7 +1092,7 @@ cmServerResponse cmServerProtocol1_0::ProcessSetGlobalSettings( kWARN_UNINITIALIZED_KEY, kWARN_UNUSED_KEY, kWARN_UNUSED_CLI_KEY, kCHECK_SYSTEM_VARS_KEY }; - for (auto i : boolValues) { + for (const auto& i : boolValues) { if (!request.Data[i].isNull() && !request.Data[i].isBool()) { return request.ReportError("\"" + i + "\" must be unset or a bool value."); diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index d83662e..5a09718 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -116,6 +116,10 @@ cmVisualStudio10TargetGenerator::~cmVisualStudio10TargetGenerator() i != this->CudaOptions.end(); ++i) { delete i->second; } + for (OptionsMap::iterator i = this->CudaLinkOptions.begin(); + i != this->CudaLinkOptions.end(); ++i) { + delete i->second; + } if (!this->BuildFileStream) { return; } @@ -213,6 +217,9 @@ void cmVisualStudio10TargetGenerator::Generate() if (!this->ComputeCudaOptions()) { return; } + if (!this->ComputeCudaLinkOptions()) { + return; + } if (!this->ComputeMasmOptions()) { return; } @@ -2524,6 +2531,70 @@ void cmVisualStudio10TargetGenerator::WriteCudaOptions( this->WriteString("</CudaCompile>\n", 2); } +bool cmVisualStudio10TargetGenerator::ComputeCudaLinkOptions() +{ + if (!this->GlobalGenerator->IsCudaEnabled()) { + return true; + } + for (std::vector<std::string>::const_iterator i = + this->Configurations.begin(); + i != this->Configurations.end(); ++i) { + if (!this->ComputeCudaLinkOptions(*i)) { + return false; + } + } + return true; +} + +bool cmVisualStudio10TargetGenerator::ComputeCudaLinkOptions( + std::string const& configName) +{ + cmGlobalVisualStudio10Generator* gg = + static_cast<cmGlobalVisualStudio10Generator*>(this->GlobalGenerator); + CM_AUTO_PTR<Options> pOptions(new Options( + this->LocalGenerator, Options::CudaCompiler, gg->GetCudaFlagTable())); + Options& cudaLinkOptions = *pOptions; + + // Determine if we need to do a device link + bool doDeviceLinking = false; + switch (this->GeneratorTarget->GetType()) { + case cmStateEnums::SHARED_LIBRARY: + case cmStateEnums::MODULE_LIBRARY: + case cmStateEnums::EXECUTABLE: + doDeviceLinking = true; + break; + case cmStateEnums::STATIC_LIBRARY: + doDeviceLinking = this->GeneratorTarget->GetPropertyAsBool( + "CUDA_RESOLVE_DEVICE_SYMBOLS"); + break; + default: + break; + } + + cudaLinkOptions.AddFlag("PerformDeviceLink", + doDeviceLinking ? "true" : "false"); + + this->CudaLinkOptions[configName] = pOptions.release(); + return true; +} + +void cmVisualStudio10TargetGenerator::WriteCudaLinkOptions( + std::string const& configName) +{ + if (this->GeneratorTarget->GetType() > cmStateEnums::MODULE_LIBRARY) { + return; + } + + if (!this->MSTools || !this->GlobalGenerator->IsCudaEnabled()) { + return; + } + + this->WriteString("<CudaLink>\n", 2); + Options& cudaLinkOptions = *(this->CudaLinkOptions[configName]); + cudaLinkOptions.OutputFlagMap(*this->BuildFileStream, " "); + this->WriteString("</CudaLink>\n", 2); +} + bool cmVisualStudio10TargetGenerator::ComputeMasmOptions() { if (!this->GlobalGenerator->IsMasmEnabled()) { @@ -3283,6 +3354,7 @@ void cmVisualStudio10TargetGenerator::WriteItemDefinitionGroups() } // output link flags <Link></Link> this->WriteLinkOptions(*i); + this->WriteCudaLinkOptions(*i); // output lib flags <Lib></Lib> this->WriteLibOptions(*i); // output manifest flags <Manifest></Manifest> diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h index bd270bf..6106615 100644 --- a/Source/cmVisualStudio10TargetGenerator.h +++ b/Source/cmVisualStudio10TargetGenerator.h @@ -101,6 +101,11 @@ private: bool ComputeCudaOptions(std::string const& config); void WriteCudaOptions(std::string const& config, std::vector<std::string> const& includes); + + bool ComputeCudaLinkOptions(); + bool ComputeCudaLinkOptions(std::string const& config); + void WriteCudaLinkOptions(std::string const& config); + bool ComputeMasmOptions(); bool ComputeMasmOptions(std::string const& config); void WriteMasmOptions(std::string const& config, @@ -154,6 +159,7 @@ private: OptionsMap ClOptions; OptionsMap RcOptions; OptionsMap CudaOptions; + OptionsMap CudaLinkOptions; OptionsMap MasmOptions; OptionsMap NasmOptions; OptionsMap LinkOptions; diff --git a/Tests/Cuda/Complex/dynamic.cu b/Tests/Cuda/Complex/dynamic.cu index f677868..a76973d 100644 --- a/Tests/Cuda/Complex/dynamic.cu +++ b/Tests/Cuda/Complex/dynamic.cu @@ -37,7 +37,7 @@ EXPORT int choose_cuda_device() << std::endl; return 1; } - if (prop.major >= 4) { + if (prop.major >= 3) { err = cudaSetDevice(i); if (err != cudaSuccess) { std::cout << "Could not select CUDA device " << i << std::endl; diff --git a/Tests/CudaOnly/CMakeLists.txt b/Tests/CudaOnly/CMakeLists.txt index a3bd707..5f456fc 100644 --- a/Tests/CudaOnly/CMakeLists.txt +++ b/Tests/CudaOnly/CMakeLists.txt @@ -3,3 +3,4 @@ ADD_TEST_MACRO(CudaOnly.EnableStandard CudaOnlyEnableStandard) ADD_TEST_MACRO(CudaOnly.ExportPTX CudaOnlyExportPTX) ADD_TEST_MACRO(CudaOnly.SeparateCompilation CudaOnlySeparateCompilation) ADD_TEST_MACRO(CudaOnly.WithDefs CudaOnlyWithDefs) +ADD_TEST_MACRO(CudaOnly.ResolveDeviceSymbols CudaOnlyResolveDeviceSymbols) diff --git a/Tests/CudaOnly/ResolveDeviceSymbols/CMakeLists.txt b/Tests/CudaOnly/ResolveDeviceSymbols/CMakeLists.txt new file mode 100644 index 0000000..b96bb98 --- /dev/null +++ b/Tests/CudaOnly/ResolveDeviceSymbols/CMakeLists.txt @@ -0,0 +1,52 @@ +cmake_minimum_required(VERSION 3.7) +project (CudaOnlyResolveDeviceSymbols CUDA) + +# Find nm and dumpbin +if(CMAKE_NM) + set(dump_command ${CMAKE_NM}) + set(dump_args -g) +else() + include(GetPrerequisites) + message(STATUS "calling list_prerequisites to find dumpbin") + list_prerequisites("${CMAKE_COMMAND}" 0 0 0) + if(gp_dumpbin) + set(dump_command ${gp_dumpbin}) + set(dump_args /ARCHIVEMEMBERS) + endif() +endif() + +#Goal for this example: +#Build a static library that defines multiple methods and kernels that +#use each other. +#Use a custom command to build an executable that uses this static library +#We do these together to verify that we can get a static library to do +#device symbol linking, and not have it done when the executable is made +string(APPEND CMAKE_CUDA_FLAGS " -gencode arch=compute_30,code=compute_30") +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CUDA_STANDARD 11) + +add_library(CUDAResolveDeviceLib STATIC file1.cu file2.cu) +set_target_properties(CUDAResolveDeviceLib + PROPERTIES + CUDA_SEPARABLE_COMPILATION ON + CUDA_RESOLVE_DEVICE_SYMBOLS ON + POSITION_INDEPENDENT_CODE ON) + +if(dump_command) +add_custom_command(TARGET CUDAResolveDeviceLib POST_BUILD + COMMAND ${CMAKE_COMMAND} + -DDUMP_COMMAND=${dump_command} + -DDUMP_ARGS=${dump_args} + -DTEST_LIBRARY_PATH=$<TARGET_FILE:CUDAResolveDeviceLib> + -P ${CMAKE_CURRENT_SOURCE_DIR}/verify.cmake + ) +endif() + +add_executable(CudaOnlyResolveDeviceSymbols main.cu) +target_link_libraries(CudaOnlyResolveDeviceSymbols PRIVATE CUDAResolveDeviceLib) + +if(APPLE) + # We need to add the default path to the driver (libcuda.dylib) as an rpath, so that + # the static cuda runtime can find it at runtime. + target_link_libraries(CudaOnlyResolveDeviceSymbols PRIVATE -Wl,-rpath,/usr/local/cuda/lib) +endif() diff --git a/Tests/CudaOnly/ResolveDeviceSymbols/file1.cu b/Tests/CudaOnly/ResolveDeviceSymbols/file1.cu new file mode 100644 index 0000000..1ce63bf --- /dev/null +++ b/Tests/CudaOnly/ResolveDeviceSymbols/file1.cu @@ -0,0 +1,10 @@ + +#include "file1.h" + +result_type __device__ file1_func(int x) +{ + result_type r; + r.input = x; + r.sum = x * x; + return r; +} diff --git a/Tests/CudaOnly/ResolveDeviceSymbols/file1.h b/Tests/CudaOnly/ResolveDeviceSymbols/file1.h new file mode 100644 index 0000000..ff1945c --- /dev/null +++ b/Tests/CudaOnly/ResolveDeviceSymbols/file1.h @@ -0,0 +1,7 @@ + +#pragma once +struct result_type +{ + int input; + int sum; +}; diff --git a/Tests/CudaOnly/ResolveDeviceSymbols/file2.cu b/Tests/CudaOnly/ResolveDeviceSymbols/file2.cu new file mode 100644 index 0000000..278fd6c --- /dev/null +++ b/Tests/CudaOnly/ResolveDeviceSymbols/file2.cu @@ -0,0 +1,25 @@ + +#include "file2.h" + +result_type __device__ file1_func(int x); + +result_type_dynamic __device__ file2_func(int x) +{ + const result_type r = file1_func(x); + const result_type_dynamic rd{ r.input, r.sum, true }; + return rd; +} + +static __global__ void file2_kernel(result_type_dynamic& r, int x) +{ + // call static_func which is a method that is defined in the + // static library that is always out of date + r = file2_func(x); +} + +int file2_launch_kernel(int x) +{ + result_type_dynamic r; + file2_kernel<<<1, 1>>>(r, x); + return r.sum; +} diff --git a/Tests/CudaOnly/ResolveDeviceSymbols/file2.h b/Tests/CudaOnly/ResolveDeviceSymbols/file2.h new file mode 100644 index 0000000..d2dbaa4 --- /dev/null +++ b/Tests/CudaOnly/ResolveDeviceSymbols/file2.h @@ -0,0 +1,10 @@ + +#pragma once +#include "file1.h" + +struct result_type_dynamic +{ + int input; + int sum; + bool from_static; +}; diff --git a/Tests/CudaOnly/ResolveDeviceSymbols/main.cu b/Tests/CudaOnly/ResolveDeviceSymbols/main.cu new file mode 100644 index 0000000..b4b5b9e --- /dev/null +++ b/Tests/CudaOnly/ResolveDeviceSymbols/main.cu @@ -0,0 +1,85 @@ + +#include <iostream> + +#include "file1.h" +#include "file2.h" + +int file2_launch_kernel(int x); + +result_type_dynamic __device__ file2_func(int x); +static __global__ void main_kernel(result_type_dynamic& r, int x) +{ + // call function that was not device linked to us, this will cause + // a runtime failure of "invalid device function" + r = file2_func(x); +} + +int main_launch_kernel(int x) +{ + result_type_dynamic r; + main_kernel<<<1, 1>>>(r, x); + return r.sum; +} + +int choose_cuda_device() +{ + int nDevices = 0; + cudaError_t err = cudaGetDeviceCount(&nDevices); + if (err != cudaSuccess) { + std::cerr << "Failed to retrieve the number of CUDA enabled devices" + << std::endl; + return 1; + } + for (int i = 0; i < nDevices; ++i) { + cudaDeviceProp prop; + cudaError_t err = cudaGetDeviceProperties(&prop, i); + if (err != cudaSuccess) { + std::cerr << "Could not retrieve properties from CUDA device " << i + << std::endl; + return 1; + } + std::cout << "prop.major: " << prop.major << std::endl; + if (prop.major >= 3) { + err = cudaSetDevice(i); + if (err != cudaSuccess) { + std::cout << "Could not select CUDA device " << i << std::endl; + } else { + return 0; + } + } + } + + std::cout << "Could not find a CUDA enabled card supporting compute >=3.0" + << std::endl; + + return 1; +} + +int main(int argc, char** argv) +{ + int ret = choose_cuda_device(); + if (ret) { + return 0; + } + + cudaError_t err; + file2_launch_kernel(42); + err = cudaGetLastError(); + if (err != cudaSuccess) { + std::cerr << "file2_launch_kernel: kernel launch failed: " + << cudaGetErrorString(err) << std::endl; + return 1; + } + + main_launch_kernel(1); + err = cudaGetLastError(); + if (err == cudaSuccess) { + // This kernel launch should fail as the file2_func was device linked + // into the static library and is not usable by the executable + std::cerr << "main_launch_kernel: kernel launch should have failed" + << std::endl; + return 1; + } + + return 0; +} diff --git a/Tests/CudaOnly/ResolveDeviceSymbols/verify.cmake b/Tests/CudaOnly/ResolveDeviceSymbols/verify.cmake new file mode 100644 index 0000000..94d388b --- /dev/null +++ b/Tests/CudaOnly/ResolveDeviceSymbols/verify.cmake @@ -0,0 +1,14 @@ +execute_process(COMMAND ${DUMP_COMMAND} ${DUMP_ARGS} ${TEST_LIBRARY_PATH} + RESULT_VARIABLE RESULT + OUTPUT_VARIABLE OUTPUT + ERROR_VARIABLE ERROR +) + +if(NOT "${RESULT}" STREQUAL "0") + message(FATAL_ERROR "${DUMP_COMMAND} failed [${RESULT}] [${OUTPUT}] [${ERROR}]") +endif() + +if(NOT "${OUTPUT}" MATCHES "(cmake_device_link|device-link)") + message(FATAL_ERROR + "No cuda device objects found, device linking did not occur") +endif() diff --git a/Tests/CudaOnly/SeparateCompilation/CMakeLists.txt b/Tests/CudaOnly/SeparateCompilation/CMakeLists.txt index 420d7a9..0a2542a 100644 --- a/Tests/CudaOnly/SeparateCompilation/CMakeLists.txt +++ b/Tests/CudaOnly/SeparateCompilation/CMakeLists.txt @@ -12,6 +12,7 @@ project (CudaOnlySeparateCompilation CUDA) string(APPEND CMAKE_CUDA_FLAGS " -gencode arch=compute_30,code=compute_30") set(CMAKE_CXX_STANDARD 11) set(CMAKE_CUDA_STANDARD 11) + add_library(CUDASeparateLibA STATIC file1.cu file2.cu file3.cu) #Having file4/file5 in a shared library causes serious problems @@ -22,12 +23,24 @@ add_library(CUDASeparateLibB STATIC file4.cu file5.cu) target_link_libraries(CUDASeparateLibB PRIVATE CUDASeparateLibA) add_executable(CudaOnlySeparateCompilation main.cu) -target_link_libraries(CudaOnlySeparateCompilation PRIVATE CUDASeparateLibB) +target_link_libraries(CudaOnlySeparateCompilation + PRIVATE CUDASeparateLibB) + +set_target_properties(CUDASeparateLibA + CUDASeparateLibB + PROPERTIES CUDA_SEPARABLE_COMPILATION ON + POSITION_INDEPENDENT_CODE ON) -set_target_properties( CUDASeparateLibA - CUDASeparateLibB - PROPERTIES CUDA_SEPARABLE_COMPILATION ON) +if (CMAKE_GENERATOR MATCHES "^Visual Studio") + #Visual Studio CUDA integration will not perform device linking + #on a target that itself does not have GenerateRelocatableDeviceCode + #enabled. + set_target_properties(CudaOnlySeparateCompilation + PROPERTIES CUDA_SEPARABLE_COMPILATION ON) +endif() -set_target_properties( CUDASeparateLibA - CUDASeparateLibB - PROPERTIES POSITION_INDEPENDENT_CODE ON) +if (APPLE) + # We need to add the default path to the driver (libcuda.dylib) as an rpath, so that + # the static cuda runtime can find it at runtime. + target_link_libraries(CudaOnlySeparateCompilation PRIVATE -Wl,-rpath,/usr/local/cuda/lib) +endif() diff --git a/Tests/CudaOnly/SeparateCompilation/main.cu b/Tests/CudaOnly/SeparateCompilation/main.cu index 03e0921..40dbe5d 100644 --- a/Tests/CudaOnly/SeparateCompilation/main.cu +++ b/Tests/CudaOnly/SeparateCompilation/main.cu @@ -7,9 +7,62 @@ int file4_launch_kernel(int x); int file5_launch_kernel(int x); +int choose_cuda_device() +{ + int nDevices = 0; + cudaError_t err = cudaGetDeviceCount(&nDevices); + if (err != cudaSuccess) { + std::cerr << "Failed to retrieve the number of CUDA enabled devices" + << std::endl; + return 1; + } + for (int i = 0; i < nDevices; ++i) { + cudaDeviceProp prop; + cudaError_t err = cudaGetDeviceProperties(&prop, i); + if (err != cudaSuccess) { + std::cerr << "Could not retrieve properties from CUDA device " << i + << std::endl; + return 1; + } + if (prop.major >= 3) { + err = cudaSetDevice(i); + if (err != cudaSuccess) { + std::cout << "Could not select CUDA device " << i << std::endl; + } else { + return 0; + } + } + } + + std::cout << "Could not find a CUDA enabled card supporting compute >=3.0" + << std::endl; + + return 1; +} + int main(int argc, char** argv) { + int ret = choose_cuda_device(); + if (ret) { + return 0; + } + + cudaError_t err; file4_launch_kernel(42); + err = cudaGetLastError(); + if (err != cudaSuccess) { + std::cerr << "file4_launch_kernel: kernel launch failed: " + << cudaGetErrorString(err) << std::endl; + return 1; + } + file5_launch_kernel(42); + err = cudaGetLastError(); + if (err != cudaSuccess) { + std::cerr << "file5_launch_kernel: kernel launch failed: " + << cudaGetErrorString(err) << std::endl; + return 1; + } + return 0; } diff --git a/Tests/RunCMake/AutoExportDll/foo.c b/Tests/RunCMake/AutoExportDll/foo.c index 4b1318b..e70fbb5 100644 --- a/Tests/RunCMake/AutoExportDll/foo.c +++ b/Tests/RunCMake/AutoExportDll/foo.c @@ -13,3 +13,5 @@ int bar() { return 5; } + +const char testconst[] = "testconst"; diff --git a/Tests/RunCMake/AutoExportDll/say.cxx b/Tests/RunCMake/AutoExportDll/say.cxx index 51060e8..eb9c0ff 100644 --- a/Tests/RunCMake/AutoExportDll/say.cxx +++ b/Tests/RunCMake/AutoExportDll/say.cxx @@ -13,6 +13,14 @@ int WINAPI foo(); int bar(); int objlib(); void justnop(); + +// test const export +#ifdef _WIN32 +// data symbols must be explicitly imported +__declspec(dllimport) extern const char testconst[]; +#else +extern const char testconst[]; +#endif } // test c++ functions @@ -43,6 +51,8 @@ int main() bar(); objlib(); printf("\n"); + printf("%s", testconst); + printf("\n"); #ifdef HAS_JUSTNOP justnop(); #endif diff --git a/Tests/RunCMake/BuildDepends/MakeCustomIncludes.cmake b/Tests/RunCMake/BuildDepends/MakeCustomIncludes.cmake new file mode 100644 index 0000000..0f92e0e --- /dev/null +++ b/Tests/RunCMake/BuildDepends/MakeCustomIncludes.cmake @@ -0,0 +1,13 @@ +add_custom_command( + OUTPUT output.cxx + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/MakeCustomIncludes.cxx output.cxx + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/MakeCustomIncludes.cxx + IMPLICIT_DEPENDS CXX ${CMAKE_CURRENT_SOURCE_DIR}/MakeCustomIncludes.cxx) +add_custom_target(generate ALL DEPENDS output.cxx) +set_property(TARGET generate PROPERTY INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR}) + +file(GENERATE OUTPUT check-$<LOWER_CASE:$<CONFIG>>.cmake CONTENT " +set(check_pairs + \"${CMAKE_CURRENT_BINARY_DIR}/output.cxx|${CMAKE_CURRENT_BINARY_DIR}/MakeCustomIncludes.h\" + ) +") diff --git a/Tests/RunCMake/BuildDepends/MakeCustomIncludes.cxx b/Tests/RunCMake/BuildDepends/MakeCustomIncludes.cxx new file mode 100644 index 0000000..9a0edef --- /dev/null +++ b/Tests/RunCMake/BuildDepends/MakeCustomIncludes.cxx @@ -0,0 +1,6 @@ +#include "MakeCustomIncludes.h" + +int main() +{ + return MakeCustomIncludes(); +} diff --git a/Tests/RunCMake/BuildDepends/MakeCustomIncludes.step1.cmake b/Tests/RunCMake/BuildDepends/MakeCustomIncludes.step1.cmake new file mode 100644 index 0000000..6bb01a6 --- /dev/null +++ b/Tests/RunCMake/BuildDepends/MakeCustomIncludes.step1.cmake @@ -0,0 +1,3 @@ +file(WRITE "${RunCMake_TEST_BINARY_DIR}/MakeCustomIncludes.h" [[ +inline int MakeCustomIncludes() { return 1; } +]]) diff --git a/Tests/RunCMake/BuildDepends/MakeCustomIncludes.step2.cmake b/Tests/RunCMake/BuildDepends/MakeCustomIncludes.step2.cmake new file mode 100644 index 0000000..6b3151d --- /dev/null +++ b/Tests/RunCMake/BuildDepends/MakeCustomIncludes.step2.cmake @@ -0,0 +1,3 @@ +file(WRITE "${RunCMake_TEST_BINARY_DIR}/MakeCustomIncludes.h" [[ +inline int MakeCustomIncludes() { return 2; } +]]) diff --git a/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake b/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake index 67a6101..9941c70 100644 --- a/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake +++ b/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake @@ -43,9 +43,11 @@ endif() run_BuildDepends(Custom-Symbolic-and-Byproduct) run_BuildDepends(Custom-Always) -if(RunCMake_GENERATOR MATCHES "Make" AND - NOT "${RunCMake_BINARY_DIR}" STREQUAL "${RunCMake_SOURCE_DIR}") - run_BuildDepends(MakeInProjectOnly) +if(RunCMake_GENERATOR MATCHES "Make") + run_BuildDepends(MakeCustomIncludes) + if(NOT "${RunCMake_BINARY_DIR}" STREQUAL "${RunCMake_SOURCE_DIR}") + run_BuildDepends(MakeInProjectOnly) + endif() endif() function(run_ReGeneration) diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 0e17bb3..75d4e29 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -199,6 +199,7 @@ add_RunCMake_test(ctest_start) add_RunCMake_test(ctest_submit) add_RunCMake_test(ctest_test) add_RunCMake_test(ctest_disabled_test) +add_RunCMake_test(ctest_skipped_test) add_RunCMake_test(ctest_upload) add_RunCMake_test(ctest_fixtures) add_RunCMake_test(file) diff --git a/Tests/RunCMake/ctest_disabled_test/DisableCleanupTest-stdout.txt b/Tests/RunCMake/ctest_disabled_test/DisableCleanupTest-stdout.txt index ee0dc51..9449e65 100644 --- a/Tests/RunCMake/ctest_disabled_test/DisableCleanupTest-stdout.txt +++ b/Tests/RunCMake/ctest_disabled_test/DisableCleanupTest-stdout.txt @@ -7,5 +7,5 @@ + Total Test time \(real\) = +[0-9.]+ sec + -The following tests are disabled and did not run: -.*2 \- CleanupTest +The following tests did not run: +.*2 \- CleanupTest \(Disabled\) diff --git a/Tests/RunCMake/ctest_disabled_test/DisableFailingTest-stdout.txt b/Tests/RunCMake/ctest_disabled_test/DisableFailingTest-stdout.txt index e2c9f92..486722e 100644 --- a/Tests/RunCMake/ctest_disabled_test/DisableFailingTest-stdout.txt +++ b/Tests/RunCMake/ctest_disabled_test/DisableFailingTest-stdout.txt @@ -2,8 +2,8 @@ + Total Test time \(real\) = +[0-9.]+ sec + -The following tests are disabled and did not run: -.*3 \- DisabledFailingTest +The following tests did not run: +.*3 \- DisabledFailingTest \(Disabled\) + The following tests FAILED: .*2 \- FailingTest \(Failed\) diff --git a/Tests/RunCMake/ctest_disabled_test/DisableNotRunTest-stdout.txt b/Tests/RunCMake/ctest_disabled_test/DisableNotRunTest-stdout.txt index d8bf966..9078aeb 100644 --- a/Tests/RunCMake/ctest_disabled_test/DisableNotRunTest-stdout.txt +++ b/Tests/RunCMake/ctest_disabled_test/DisableNotRunTest-stdout.txt @@ -10,8 +10,8 @@ + Total Test time \(real\) = +[0-9.]+ sec + -The following tests are disabled and did not run: -.*2 \- DisabledTest +The following tests did not run: +.*2 \- DisabledTest \(Disabled\) + The following tests FAILED: .*3 - NotRunTest \(Not Run\) diff --git a/Tests/RunCMake/ctest_disabled_test/DisableRequiredTest-stdout.txt b/Tests/RunCMake/ctest_disabled_test/DisableRequiredTest-stdout.txt index 886efb8..10d385e 100644 --- a/Tests/RunCMake/ctest_disabled_test/DisableRequiredTest-stdout.txt +++ b/Tests/RunCMake/ctest_disabled_test/DisableRequiredTest-stdout.txt @@ -9,5 +9,5 @@ + Total Test time \(real\) = +[0-9.]+ sec + -The following tests are disabled and did not run: -.*2 \- DisabledTest +The following tests did not run: +.*2 \- DisabledTest \(Disabled\) diff --git a/Tests/RunCMake/ctest_disabled_test/DisableSetupTest-stdout.txt b/Tests/RunCMake/ctest_disabled_test/DisableSetupTest-stdout.txt index dc27950..2dfd10d 100644 --- a/Tests/RunCMake/ctest_disabled_test/DisableSetupTest-stdout.txt +++ b/Tests/RunCMake/ctest_disabled_test/DisableSetupTest-stdout.txt @@ -9,5 +9,5 @@ + Total Test time \(real\) = +[0-9.]+ sec + -The following tests are disabled and did not run: -.*2 \- DisabledTest +The following tests did not run: +.*2 \- DisabledTest \(Disabled\) diff --git a/Tests/RunCMake/ctest_disabled_test/DisabledTest-stdout.txt b/Tests/RunCMake/ctest_disabled_test/DisabledTest-stdout.txt index d8bf966..9078aeb 100644 --- a/Tests/RunCMake/ctest_disabled_test/DisabledTest-stdout.txt +++ b/Tests/RunCMake/ctest_disabled_test/DisabledTest-stdout.txt @@ -10,8 +10,8 @@ + Total Test time \(real\) = +[0-9.]+ sec + -The following tests are disabled and did not run: -.*2 \- DisabledTest +The following tests did not run: +.*2 \- DisabledTest \(Disabled\) + The following tests FAILED: .*3 - NotRunTest \(Not Run\) diff --git a/Tests/RunCMake/ctest_skipped_test/CMakeLists.txt.in b/Tests/RunCMake/ctest_skipped_test/CMakeLists.txt.in new file mode 100644 index 0000000..cc4b8ed --- /dev/null +++ b/Tests/RunCMake/ctest_skipped_test/CMakeLists.txt.in @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.8) +project(@CASE_NAME@ C) +include(CTest) + +if (WIN32) + set(skip_command "@CMAKE_CURRENT_LIST_DIR@/skip.bat") +else () + set(skip_command "@CMAKE_CURRENT_LIST_DIR@/skip.sh") +endif () + +add_test(NAME SuccessfulTest COMMAND "${CMAKE_COMMAND}" --version) +@CASE_CMAKELISTS_SUFFIX_CODE@ diff --git a/Tests/RunCMake/ctest_skipped_test/CTestConfig.cmake.in b/Tests/RunCMake/ctest_skipped_test/CTestConfig.cmake.in new file mode 100644 index 0000000..c0d7e42 --- /dev/null +++ b/Tests/RunCMake/ctest_skipped_test/CTestConfig.cmake.in @@ -0,0 +1 @@ +set(CTEST_PROJECT_NAME "@CASE_NAME@") diff --git a/Tests/RunCMake/ctest_skipped_test/RunCMakeTest.cmake b/Tests/RunCMake/ctest_skipped_test/RunCMakeTest.cmake new file mode 100644 index 0000000..dcf5cd4 --- /dev/null +++ b/Tests/RunCMake/ctest_skipped_test/RunCMakeTest.cmake @@ -0,0 +1,51 @@ +include(RunCTest) + +function(run_SkipTest) + set(CASE_CMAKELISTS_SUFFIX_CODE [[ +add_test(NAME SkipTest COMMAND ${skip_command}) + +set_tests_properties(SkipTest PROPERTIES SKIP_RETURN_CODE 125) + ]]) + run_ctest(SkipTest) +endfunction() +run_SkipTest() + +function(run_SkipSetupTest) + set(CASE_CMAKELISTS_SUFFIX_CODE [[ +add_test(NAME SkipTest COMMAND ${skip_command}) +add_test(NAME SuccessfulCleanupTest COMMAND "${CMAKE_COMMAND}" --version) + +set_tests_properties(SkipTest PROPERTIES SKIP_RETURN_CODE 125 + FIXTURES_SETUP "Foo") +set_tests_properties(SuccessfulTest PROPERTIES FIXTURES_REQUIRED "Foo") +set_tests_properties(SuccessfulCleanupTest PROPERTIES FIXTURES_CLEANUP "Foo") + ]]) + run_ctest(SkipSetupTest) +endfunction() +run_SkipSetupTest() + +function(run_SkipRequiredTest) + set(CASE_CMAKELISTS_SUFFIX_CODE [[ +add_test(NAME SkipTest COMMAND ${skip_command}) +add_test(NAME SuccessfulCleanupTest COMMAND "${CMAKE_COMMAND}" --version) + +set_tests_properties(SuccessfulTest PROPERTIES FIXTURES_SETUP "Foo") +set_tests_properties(SkipTest PROPERTIES SKIP_RETURN_CODE 125 + FIXTURES_REQUIRED "Foo") +set_tests_properties(SuccessfulCleanupTest PROPERTIES FIXTURES_CLEANUP "Foo") + ]]) + run_ctest(SkipRequiredTest) +endfunction() +run_SkipRequiredTest() + +function(run_SkipCleanupTest) + set(CASE_CMAKELISTS_SUFFIX_CODE [[ +add_test(NAME CleanupTest COMMAND ${skip_command}) + +set_tests_properties(SuccessfulTest PROPERTIES FIXTURES_REQUIRED "Foo") +set_tests_properties(CleanupTest PROPERTIES SKIP_RETURN_CODE 125 + FIXTURES_CLEANUP "Foo") + ]]) + run_ctest(SkipCleanupTest) +endfunction() +run_SkipCleanupTest() diff --git a/Tests/RunCMake/ctest_skipped_test/SkipCleanupTest-stdout.txt b/Tests/RunCMake/ctest_skipped_test/SkipCleanupTest-stdout.txt new file mode 100644 index 0000000..3b14b7a --- /dev/null +++ b/Tests/RunCMake/ctest_skipped_test/SkipCleanupTest-stdout.txt @@ -0,0 +1,11 @@ + Start 1: SuccessfulTest +1/2 Test #1: SuccessfulTest ................... Passed +[0-9.]+ sec + Start 2: CleanupTest +2/2 Test #2: CleanupTest ......................\*\*\*\Skipped +[0-9.]+ sec ++ +100% tests passed, 0 tests failed out of 2 ++ +Total Test time \(real\) = +[0-9.]+ sec ++ +The following tests did not run: +.*2 \- CleanupTest \(Skipped\) diff --git a/Tests/RunCMake/ctest_skipped_test/SkipRequiredTest-stdout.txt b/Tests/RunCMake/ctest_skipped_test/SkipRequiredTest-stdout.txt new file mode 100644 index 0000000..8ecc6e3 --- /dev/null +++ b/Tests/RunCMake/ctest_skipped_test/SkipRequiredTest-stdout.txt @@ -0,0 +1,13 @@ + Start 1: SuccessfulTest +1/3 Test #1: SuccessfulTest ................... Passed +[0-9.]+ sec + Start 2: SkipTest +2/3 Test #2: SkipTest .........................\*\*\*\Skipped +[0-9.]+ sec + Start 3: SuccessfulCleanupTest +3/3 Test #3: SuccessfulCleanupTest ............ Passed +[0-9.]+ sec ++ +100% tests passed, 0 tests failed out of 3 ++ +Total Test time \(real\) = +[0-9.]+ sec ++ +The following tests did not run: +.*2 \- SkipTest \(Skipped\) diff --git a/Tests/RunCMake/ctest_skipped_test/SkipSetupTest-stdout.txt b/Tests/RunCMake/ctest_skipped_test/SkipSetupTest-stdout.txt new file mode 100644 index 0000000..fe9bf34 --- /dev/null +++ b/Tests/RunCMake/ctest_skipped_test/SkipSetupTest-stdout.txt @@ -0,0 +1,13 @@ + Start 2: SkipTest +1/3 Test #2: SkipTest .........................\*\*\*\Skipped +[0-9.]+ sec + Start 1: SuccessfulTest +2/3 Test #1: SuccessfulTest ................... Passed +[0-9.]+ sec + Start 3: SuccessfulCleanupTest +3/3 Test #3: SuccessfulCleanupTest ............ Passed +[0-9.]+ sec ++ +100% tests passed, 0 tests failed out of 3 ++ +Total Test time \(real\) = +[0-9.]+ sec ++ +The following tests did not run: +.*2 \- SkipTest \(Skipped\) diff --git a/Tests/RunCMake/ctest_skipped_test/SkipTest-stdout.txt b/Tests/RunCMake/ctest_skipped_test/SkipTest-stdout.txt new file mode 100644 index 0000000..52e7a0b --- /dev/null +++ b/Tests/RunCMake/ctest_skipped_test/SkipTest-stdout.txt @@ -0,0 +1,11 @@ + Start 1: SuccessfulTest +1/2 Test #1: SuccessfulTest ................... Passed +[0-9.]+ sec + Start 2: SkipTest +2/2 Test #2: SkipTest .........................\*\*\*\Skipped +[0-9.]+ sec ++ +100% tests passed, 0 tests failed out of 2 ++ +Total Test time \(real\) = +[0-9.]+ sec ++ +The following tests did not run: +.*2 \- SkipTest \(Skipped\) diff --git a/Tests/RunCMake/ctest_skipped_test/skip.bat b/Tests/RunCMake/ctest_skipped_test/skip.bat new file mode 100755 index 0000000..80e1290 --- /dev/null +++ b/Tests/RunCMake/ctest_skipped_test/skip.bat @@ -0,0 +1 @@ +EXIT 125 diff --git a/Tests/RunCMake/ctest_skipped_test/skip.sh b/Tests/RunCMake/ctest_skipped_test/skip.sh new file mode 100755 index 0000000..f9c4603 --- /dev/null +++ b/Tests/RunCMake/ctest_skipped_test/skip.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +exit 125 diff --git a/Tests/RunCMake/ctest_skipped_test/test.cmake.in b/Tests/RunCMake/ctest_skipped_test/test.cmake.in new file mode 100644 index 0000000..ca23c83 --- /dev/null +++ b/Tests/RunCMake/ctest_skipped_test/test.cmake.in @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.7) + +set(CTEST_SITE "test-site") +set(CTEST_BUILD_NAME "test-build-name") +set(CTEST_SOURCE_DIRECTORY "@RunCMake_BINARY_DIR@/@CASE_NAME@") +set(CTEST_BINARY_DIRECTORY "@RunCMake_BINARY_DIR@/@CASE_NAME@-build") +set(CTEST_CMAKE_GENERATOR "@RunCMake_GENERATOR@") +set(CTEST_CMAKE_GENERATOR_PLATFORM "@RunCMake_GENERATOR_PLATFORM@") +set(CTEST_CMAKE_GENERATOR_TOOLSET "@RunCMake_GENERATOR_TOOLSET@") +set(CTEST_BUILD_CONFIGURATION "$ENV{CMAKE_CONFIG_TYPE}") + +set(ctest_test_args "@CASE_CTEST_TEST_ARGS@") +ctest_start(Experimental) +ctest_configure() +ctest_build() +ctest_test(${ctest_test_args}) diff --git a/Tests/RunCMake/install/FILES-TARGET_OBJECTS-all-check.cmake b/Tests/RunCMake/install/FILES-TARGET_OBJECTS-all-check.cmake new file mode 100644 index 0000000..f7f2a3a --- /dev/null +++ b/Tests/RunCMake/install/FILES-TARGET_OBJECTS-all-check.cmake @@ -0,0 +1 @@ +check_installed([[^objs;objs/obj1(\.c)?\.(o|obj);objs/obj2(\.c)?\.(o|obj)$]]) diff --git a/Tests/RunCMake/install/FILES-TARGET_OBJECTS.cmake b/Tests/RunCMake/install/FILES-TARGET_OBJECTS.cmake new file mode 100644 index 0000000..40c58ad --- /dev/null +++ b/Tests/RunCMake/install/FILES-TARGET_OBJECTS.cmake @@ -0,0 +1,3 @@ +enable_language(C) +add_library(objs OBJECT obj1.c obj2.c) +install(FILES $<TARGET_OBJECTS:objs> DESTINATION objs) diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake index 45693b5..1a60f0c 100644 --- a/Tests/RunCMake/install/RunCMakeTest.cmake +++ b/Tests/RunCMake/install/RunCMakeTest.cmake @@ -13,12 +13,15 @@ function(run_install_test case) # Check "all" components. set(CMAKE_INSTALL_PREFIX ${RunCMake_TEST_BINARY_DIR}/root-all) run_cmake_command(${case}-all ${CMAKE_COMMAND} -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} -DBUILD_TYPE=Debug -P cmake_install.cmake) - # Check unspecified component. - set(CMAKE_INSTALL_PREFIX ${RunCMake_TEST_BINARY_DIR}/root-uns) - run_cmake_command(${case}-uns ${CMAKE_COMMAND} -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} -DBUILD_TYPE=Debug -DCOMPONENT=Unspecified -P cmake_install.cmake) - # Check explicit component. - set(CMAKE_INSTALL_PREFIX ${RunCMake_TEST_BINARY_DIR}/root-exc) - run_cmake_command(${case}-exc ${CMAKE_COMMAND} -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} -DBUILD_TYPE=Debug -DCOMPONENT=exc -P cmake_install.cmake) + + if(run_install_test_components) + # Check unspecified component. + set(CMAKE_INSTALL_PREFIX ${RunCMake_TEST_BINARY_DIR}/root-uns) + run_cmake_command(${case}-uns ${CMAKE_COMMAND} -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} -DBUILD_TYPE=Debug -DCOMPONENT=Unspecified -P cmake_install.cmake) + # Check explicit component. + set(CMAKE_INSTALL_PREFIX ${RunCMake_TEST_BINARY_DIR}/root-exc) + run_cmake_command(${case}-exc ${CMAKE_COMMAND} -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} -DBUILD_TYPE=Debug -DCOMPONENT=exc -P cmake_install.cmake) + endif() endfunction() # Function called in *-check.cmake scripts to check installed files. @@ -57,5 +60,10 @@ run_cmake(CMP0062-OLD) run_cmake(CMP0062-NEW) run_cmake(CMP0062-WARN) +if(NOT RunCMake_GENERATOR STREQUAL "Xcode" OR NOT "$ENV{CMAKE_OSX_ARCHITECTURES}" MATCHES "[;$]") + run_install_test(FILES-TARGET_OBJECTS) +endif() + +set(run_install_test_components 1) run_install_test(FILES-EXCLUDE_FROM_ALL) run_install_test(TARGETS-EXCLUDE_FROM_ALL) diff --git a/Tests/RunCMake/install/obj1.c b/Tests/RunCMake/install/obj1.c new file mode 100644 index 0000000..2411aab --- /dev/null +++ b/Tests/RunCMake/install/obj1.c @@ -0,0 +1,4 @@ +int obj1(void) +{ + return 0; +} diff --git a/Tests/RunCMake/install/obj2.c b/Tests/RunCMake/install/obj2.c new file mode 100644 index 0000000..2dad71e --- /dev/null +++ b/Tests/RunCMake/install/obj2.c @@ -0,0 +1,4 @@ +int obj2(void) +{ + return 0; +} |