From 4d8b74fc400e0d1a092b74f95ce504448787babe Mon Sep 17 00:00:00 2001 From: Union Palenshus Date: Wed, 25 Nov 2020 17:27:22 -0800 Subject: [PATCH 01/47] Adding export to WinGetUtil.dll to expose Version comparing (#652) --- src/WinGetUtil/Exports.cpp | 17 +++++++++++++++++ src/WinGetUtil/Source.def | 1 + src/WinGetUtil/WinGetUtil.h | 6 ++++++ 3 files changed, 24 insertions(+) diff --git a/src/WinGetUtil/Exports.cpp b/src/WinGetUtil/Exports.cpp index 8ffd7a8c1e..0e416a760c 100644 --- a/src/WinGetUtil/Exports.cpp +++ b/src/WinGetUtil/Exports.cpp @@ -227,4 +227,21 @@ extern "C" return S_OK; } CATCH_RETURN() + + WINGET_UTIL_API WinGetCompareVersions( + WINGET_STRING versionA, + WINGET_STRING versionB, + INT* comparisonResult) try + { + THROW_HR_IF(E_INVALIDARG, !versionA); + THROW_HR_IF(E_INVALIDARG, !versionB); + + Version vA{ ConvertToUTF8(versionA) }; + Version vB{ ConvertToUTF8(versionB) }; + + *comparisonResult = vA < vB ? -1 : (vA == vB ? 0 : 1); + + return S_OK; + } + CATCH_RETURN() } diff --git a/src/WinGetUtil/Source.def b/src/WinGetUtil/Source.def index 9cfc6d000c..727acfd0c0 100644 --- a/src/WinGetUtil/Source.def +++ b/src/WinGetUtil/Source.def @@ -12,3 +12,4 @@ EXPORTS WinGetSQLiteIndexCheckConsistency WinGetValidateManifest WinGetDownload + WinGetCompareVersions diff --git a/src/WinGetUtil/WinGetUtil.h b/src/WinGetUtil/WinGetUtil.h index c8d7799de6..e4892969e0 100644 --- a/src/WinGetUtil/WinGetUtil.h +++ b/src/WinGetUtil/WinGetUtil.h @@ -86,4 +86,10 @@ extern "C" WINGET_STRING filePath, BYTE* sha256Hash, UINT32 sha256HashLength); + + // Compares two version strings, returning -1 if versionA is less than versionB, 0 if they're equal, or 1 if versionA is greater than versionB + WINGET_UTIL_API WinGetCompareVersions( + WINGET_STRING versionA, + WINGET_STRING versionB, + INT* comparisonResult); } From 92b3e73bb85a16e02a4c3044d6693a58ea9d6f5e Mon Sep 17 00:00:00 2001 From: yao-msft <50888816+yao-msft@users.noreply.github.com> Date: Wed, 25 Nov 2020 19:28:58 -0800 Subject: [PATCH 02/47] Workflow improvements to increase winget command success rate (#648) --- .github/actions/spelling/expect.txt | 1 + src/AppInstallerCLICore/Resources.h | 4 ++ .../Workflows/InstallFlow.cpp | 65 ++++++++++++++++--- .../Workflows/WorkflowBase.cpp | 9 ++- src/AppInstallerCLIE2ETests/Constants.cs | 1 + src/AppInstallerCLIE2ETests/SourceCommand.cs | 2 +- .../Shared/Strings/en-us/winget.resw | 12 ++++ src/AppInstallerCLITests/Sources.cpp | 4 +- src/AppInstallerCommonCore/Downloader.cpp | 19 ++++-- src/AppInstallerCommonCore/Errors.cpp | 4 ++ .../HttpStream/HttpClientWrapper.cpp | 4 +- .../Public/AppInstallerDownloader.h | 3 +- .../Public/AppInstallerErrors.h | 2 + .../Public/AppInstallerSynchronization.h | 3 + .../Synchronization.cpp | 3 +- .../Microsoft/SQLiteIndexSource.cpp | 29 ++++++++- .../Public/AppInstallerRepositorySource.h | 11 +++- .../RepositorySource.cpp | 40 +++++++++--- 18 files changed, 186 insertions(+), 30 deletions(-) diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 722897a8f3..14473b620d 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -326,3 +326,4 @@ Wunused WZDNCRFJ xf xl +INET diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index 3258173277..b23aed7770 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -74,6 +74,9 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(InstallationRequiresHigherWindows); WINGET_DEFINE_RESOURCE_STRINGID(InstallCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(InstallCommandShortDescription); + WINGET_DEFINE_RESOURCE_STRINGID(InstallerBlockedByPolicy); + WINGET_DEFINE_RESOURCE_STRINGID(InstallerFailedSecurityCheck); + WINGET_DEFINE_RESOURCE_STRINGID(InstallerFailedVirusScan); WINGET_DEFINE_RESOURCE_STRINGID(InstallerHashMismatchAdminBlock); WINGET_DEFINE_RESOURCE_STRINGID(InstallerHashMismatchOverridden); WINGET_DEFINE_RESOURCE_STRINGID(InstallerHashMismatchOverrideRequired); @@ -177,6 +180,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(SourceNameArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceOpenFailedSuggestion); WINGET_DEFINE_RESOURCE_STRINGID(SourceOpenPredefinedFailedSuggestion); + WINGET_DEFINE_RESOURCE_STRINGID(SourceOpenWithFailedUpdate); WINGET_DEFINE_RESOURCE_STRINGID(SourceRemoveAll); WINGET_DEFINE_RESOURCE_STRINGID(SourceRemoveCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceRemoveCommandShortDescription); diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index 6494d866d6..48fc577c89 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -101,11 +101,40 @@ namespace AppInstaller::CLI::Workflow context.Reporter.Info() << "Downloading " << Execution::UrlEmphasis << installer.Url << std::endl; - auto hash = context.Reporter.ExecuteWithProgress(std::bind(Utility::Download, - installer.Url, - tempInstallerPath, - std::placeholders::_1, - true)); + std::optional> hash; + + const int MaxRetryCount = 2; + for (int retryCount = 0; retryCount < MaxRetryCount; ++retryCount) + { + bool success = false; + try + { + hash = context.Reporter.ExecuteWithProgress(std::bind(Utility::Download, + installer.Url, + tempInstallerPath, + std::placeholders::_1, + true)); + + success = true; + } + catch (...) + { + if (retryCount < MaxRetryCount - 1) + { + AICLI_LOG(CLI, Info, << "Failed to download, waiting a bit and retry. Url: " << installer.Url); + Sleep(500); + } + else + { + throw; + } + } + + if (success) + { + break; + } + } if (!hash) { @@ -135,9 +164,10 @@ namespace AppInstaller::CLI::Workflow } catch (const winrt::hresult_error& e) { - if (e.code() == HRESULT_FROM_WIN32(ERROR_NO_RANGES_PROCESSED)) + if (e.code() == HRESULT_FROM_WIN32(ERROR_NO_RANGES_PROCESSED) || + HRESULT_FACILITY(e.code()) == FACILITY_HTTP) { - // Server does not support range request, use download + // Failed to get signature hash through HttpStream, use download downloadInstead = true; } else @@ -210,7 +240,26 @@ namespace AppInstaller::CLI::Workflow else if (WI_IsFlagSet(context.GetFlags(), Execution::ContextFlag::InstallerHashMatched)) { const auto& installer = context.Get(); - Utility::ApplyMotwUsingIAttachmentExecuteIfApplicable(context.Get(), installer.value().Url); + HRESULT hr = Utility::ApplyMotwUsingIAttachmentExecuteIfApplicable(context.Get(), installer.value().Url); + + // Not using SUCCEEDED(hr) to check since there are cases file is missing after a successful scan + if (hr != S_OK) + { + switch (hr) + { + case INET_E_SECURITY_PROBLEM: + context.Reporter.Error() << Resource::String::InstallerBlockedByPolicy << std::endl; + break; + case E_FAIL: + context.Reporter.Error() << Resource::String::InstallerFailedVirusScan << std::endl; + break; + default: + context.Reporter.Error() << Resource::String::InstallerFailedSecurityCheck << std::endl; + } + + AICLI_LOG(Fail, Error, << "Installer failed security check. Url: " << installer.value().Url << " Result: " << WINGET_OSTREAM_FORMAT_HRESULT(hr)); + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INSTALLER_SECURITY_CHECK_FAILED); + } } } } diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index 51d585df8d..8018e1a044 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -105,7 +105,14 @@ namespace AppInstaller::CLI::Workflow std::shared_ptr source; try { - source = context.Reporter.ExecuteWithProgress(std::bind(Repository::OpenSource, sourceName, std::placeholders::_1), true); + auto result = context.Reporter.ExecuteWithProgress(std::bind(Repository::OpenSource, sourceName, std::placeholders::_1), true); + source = result.Source; + + // We'll only report the source update failure as warning and continue + for (const auto& s : result.SourcesWithUpdateFailure) + { + context.Reporter.Warn() << Resource::String::SourceOpenWithFailedUpdate << ' ' << s.Name << std::endl; + } } catch (...) { diff --git a/src/AppInstallerCLIE2ETests/Constants.cs b/src/AppInstallerCLIE2ETests/Constants.cs index 91a4500154..21bd8ff245 100644 --- a/src/AppInstallerCLIE2ETests/Constants.cs +++ b/src/AppInstallerCLIE2ETests/Constants.cs @@ -52,6 +52,7 @@ public class ErrorCode public const int ERROR_NO_RANGES_PROCESSED = unchecked((int)0x80070138); public const int OPC_E_ZIP_MISSING_END_OF_CENTRAL_DIRECTORY = unchecked((int)0x8051100f); public const int ERROR_OLD_WIN_VERSION = unchecked((int)0x8007047e); + public const int HTTP_E_STATUS_NOT_FOUND = unchecked((int)0x80190194); // AICLI custom HRESULTs public const int ERROR_INTERNAL_ERROR = unchecked((int)0x8A150001); diff --git a/src/AppInstallerCLIE2ETests/SourceCommand.cs b/src/AppInstallerCLIE2ETests/SourceCommand.cs index 5d8e97b6f7..4837eb015f 100644 --- a/src/AppInstallerCLIE2ETests/SourceCommand.cs +++ b/src/AppInstallerCLIE2ETests/SourceCommand.cs @@ -30,7 +30,7 @@ public void SourceAddWithInvalidURL() { // Add source with invalid url should fail var result = TestCommon.RunAICLICommand("source add", "AnotherSource https://microsoft.com"); - Assert.AreEqual(Constants.ErrorCode.ERROR_NO_RANGES_PROCESSED, result.ExitCode); + Assert.AreEqual(Constants.ErrorCode.HTTP_E_STATUS_NOT_FOUND, result.ExitCode); Assert.True(result.StdOut.Contains("An unexpected error occurred while executing the command")); } diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 64bd3f6ed3..7e685f343d 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -722,4 +722,16 @@ They can be configured through the settings file 'winget settings'. Logs Diagnostic files containing information about application use. + + The installer is blocked by policy + + + The installer failed security check + + + An anti-virus product reports an infection in the installer + + + Failed in attempting to update the source: + \ No newline at end of file diff --git a/src/AppInstallerCLITests/Sources.cpp b/src/AppInstallerCLITests/Sources.cpp index ecd70cbbba..aecf5b5638 100644 --- a/src/AppInstallerCLITests/Sources.cpp +++ b/src/AppInstallerCLITests/Sources.cpp @@ -510,7 +510,7 @@ TEST_CASE("RepoSources_UpdateOnOpen", "[sources]") SetSetting(Streams::UserSources, s_SingleSource); ProgressCallback progress; - auto source = OpenSource(name, progress); + auto source = OpenSource(name, progress).Source; REQUIRE(updateCalledOnFactory); @@ -574,7 +574,7 @@ TEST_CASE("RepoSources_SearchAcrossMultipleSources", "[sources]") SetSetting(Streams::UserSources, s_TwoSource_AggregateSourceTest); ProgressCallback progress; - auto source = OpenSource("", progress); + auto source = OpenSource("", progress).Source; SearchRequest request; auto result = source->Search(request); diff --git a/src/AppInstallerCommonCore/Downloader.cpp b/src/AppInstallerCommonCore/Downloader.cpp index e1ad55432f..5593ff8998 100644 --- a/src/AppInstallerCommonCore/Downloader.cpp +++ b/src/AppInstallerCommonCore/Downloader.cpp @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" +#include "Public/AppInstallerErrors.h" #include "Public/AppInstallerRuntime.h" #include "Public/AppInstallerDownloader.h" #include "Public/AppInstallerSHA256.h" @@ -109,6 +110,12 @@ namespace AppInstaller::Utility dest.flush(); + // Check download size matches if content length is provided in response header + if (contentLength > 0) + { + THROW_HR_IF(APPINSTALLER_CLI_ERROR_DOWNLOAD_SIZE_MISMATCH, bytesDownloaded != contentLength); + } + std::vector result; if (computeHash) { @@ -192,24 +199,26 @@ namespace AppInstaller::Utility AICLI_LOG(Core, Info, << "Finished applying motw"); } - void ApplyMotwUsingIAttachmentExecuteIfApplicable(const std::filesystem::path& filePath, const std::string& source) + HRESULT ApplyMotwUsingIAttachmentExecuteIfApplicable(const std::filesystem::path& filePath, const std::string& source) { AICLI_LOG(Core, Info, << "Started applying motw using IAttachmentExecute to " << filePath); if (!IsNTFS(filePath)) { AICLI_LOG(Core, Info, << "File system is not NTFS. Skipped applying motw"); - return; + return S_OK; } // Attachment execution service needs STA to succeed, so we'll create a new thread and CoInitialize with STA. + HRESULT aesSaveResult = S_OK; auto updateMotw = [&]() -> HRESULT { Microsoft::WRL::ComPtr attachmentExecute; RETURN_IF_FAILED(CoCreateInstance(CLSID_AttachmentServices, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&attachmentExecute))); RETURN_IF_FAILED(attachmentExecute->SetLocalPath(filePath.c_str())); RETURN_IF_FAILED(attachmentExecute->SetSource(Utility::ConvertToUTF16(source).c_str())); - RETURN_IF_FAILED(attachmentExecute->Save()); + aesSaveResult = attachmentExecute->Save(); + RETURN_IF_FAILED(aesSaveResult); return S_OK; }; @@ -229,6 +238,8 @@ namespace AppInstaller::Utility aesThread.join(); - AICLI_LOG(Core, Info, << "Finished applying motw using IAttachmentExecute. Result: " << hr); + AICLI_LOG(Core, Info, << "Finished applying motw using IAttachmentExecute. Result: " << hr << " IAttachmentExecute::Save() result: " << aesSaveResult); + + return aesSaveResult; } } diff --git a/src/AppInstallerCommonCore/Errors.cpp b/src/AppInstallerCommonCore/Errors.cpp index 6f83155dca..d0a9db2495 100644 --- a/src/AppInstallerCommonCore/Errors.cpp +++ b/src/AppInstallerCommonCore/Errors.cpp @@ -103,6 +103,10 @@ namespace AppInstaller return "No applicable update found"; case APPINSTALLER_CLI_ERROR_UPDATE_ALL_HAS_FAILURE: return "winget upgrade --all completed with failures"; + case APPINSTALLER_CLI_ERROR_INSTALLER_SECURITY_CHECK_FAILED: + return "Installer failed security check"; + case APPINSTALLER_CLI_ERROR_DOWNLOAD_SIZE_MISMATCH: + return "Download size does not match expected content length"; default: return "Unknown Error Code"; } diff --git a/src/AppInstallerCommonCore/HttpStream/HttpClientWrapper.cpp b/src/AppInstallerCommonCore/HttpStream/HttpClientWrapper.cpp index a5ce68e188..cb5ea7843b 100644 --- a/src/AppInstallerCommonCore/HttpStream/HttpClientWrapper.cpp +++ b/src/AppInstallerCommonCore/HttpStream/HttpClientWrapper.cpp @@ -45,7 +45,9 @@ namespace AppInstaller::Utility::HttpStream HttpResponseMessage response = co_await m_httpClient.SendRequestAsync(request, HttpCompletionOption::ResponseHeadersRead); - THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_NO_RANGES_PROCESSED), response.StatusCode() != HttpStatusCode::Ok); + THROW_HR_IF( + MAKE_HRESULT(SEVERITY_ERROR, FACILITY_HTTP, response.StatusCode()), + response.StatusCode() != HttpStatusCode::Ok); // Get the length from the response if (response.Content().Headers().HasKey(L"Content-Length")) diff --git a/src/AppInstallerCommonCore/Public/AppInstallerDownloader.h b/src/AppInstallerCommonCore/Public/AppInstallerDownloader.h index 10252c44d8..0d55061bd2 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerDownloader.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerDownloader.h @@ -45,5 +45,6 @@ namespace AppInstaller::Utility // Apply Mark of the web using IAttachmentExecute::Save if the target file is on NTFS, otherwise does nothing. // This method only does a best effort since Attachment Execution Service may be disabled. - void ApplyMotwUsingIAttachmentExecuteIfApplicable(const std::filesystem::path& filePath, const std::string& source); + // If IAttachmentExecute::Save is successfully invoked and the scan failed, the failure HRESULT is returned. + HRESULT ApplyMotwUsingIAttachmentExecuteIfApplicable(const std::filesystem::path& filePath, const std::string& source); } diff --git a/src/AppInstallerCommonCore/Public/AppInstallerErrors.h b/src/AppInstallerCommonCore/Public/AppInstallerErrors.h index 66a8d7e25e..5931c2b573 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerErrors.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerErrors.h @@ -57,6 +57,8 @@ #define APPINSTALLER_CLI_ERROR_INVALID_MANIFEST ((HRESULT)0x8A15002A) #define APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE ((HRESULT)0x8A15002B) #define APPINSTALLER_CLI_ERROR_UPDATE_ALL_HAS_FAILURE ((HRESULT)0x8A15002C) +#define APPINSTALLER_CLI_ERROR_INSTALLER_SECURITY_CHECK_FAILED ((HRESULT)0x8A15002D) +#define APPINSTALLER_CLI_ERROR_DOWNLOAD_SIZE_MISMATCH ((HRESULT)0x8A15002E) namespace AppInstaller { diff --git a/src/AppInstallerCommonCore/Public/AppInstallerSynchronization.h b/src/AppInstallerCommonCore/Public/AppInstallerSynchronization.h index c3a328ff0c..3b5774cfe2 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerSynchronization.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerSynchronization.h @@ -33,11 +33,14 @@ namespace AppInstaller::Synchronization static CrossProcessReaderWriteLock LockForWrite(std::string_view name); + bool WasAbandoned() { return m_wasAbandoned; } + private: CrossProcessReaderWriteLock(std::string_view name); wil::unique_mutex m_mutex; wil::unique_semaphore m_semaphore; ResetWhenMovedFrom m_semaphoreReleases{ 0 }; + bool m_wasAbandoned = false; }; } diff --git a/src/AppInstallerCommonCore/Synchronization.cpp b/src/AppInstallerCommonCore/Synchronization.cpp index 926fd41349..bb1bc865dc 100644 --- a/src/AppInstallerCommonCore/Synchronization.cpp +++ b/src/AppInstallerCommonCore/Synchronization.cpp @@ -46,7 +46,8 @@ namespace AppInstaller::Synchronization DWORD status = 0; auto lock = result.m_mutex.acquire(&status); - THROW_HR_IF(E_UNEXPECTED, status != WAIT_OBJECT_0); + THROW_HR_IF_NULL(E_UNEXPECTED, lock); + result.m_wasAbandoned = (status == WAIT_ABANDONED); for (LONG i = 0; i < s_CrossProcessReaderWriteLock_MaxReaders; ++i) { diff --git a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp index f5d46d3794..4520ab48d9 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp @@ -106,7 +106,34 @@ namespace AppInstaller::Repository::Microsoft AICLI_LOG(Repo, Info, << "Downloading manifest"); ProgressCallback emptyCallback; - (void)Utility::DownloadToStream(fullPath, manifestStream, emptyCallback); + + const int MaxRetryCount = 2; + for (int retryCount = 0; retryCount < MaxRetryCount; ++retryCount) + { + bool success = false; + try + { + (void)Utility::DownloadToStream(fullPath, manifestStream, emptyCallback); + success = true; + } + catch (...) + { + if (retryCount < MaxRetryCount - 1) + { + AICLI_LOG(Repo, Info, << "Downloading manifest failed, waiting a bit and retrying: " << fullPath); + Sleep(500); + } + else + { + throw; + } + } + + if (success) + { + break; + } + } std::string manifestContents = manifestStream.str(); AICLI_LOG(Repo, Verbose, << "Manifest contents: " << manifestContents); diff --git a/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySource.h b/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySource.h index 77b08f1e81..86176a0220 100644 --- a/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySource.h +++ b/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySource.h @@ -97,9 +97,18 @@ namespace AppInstaller::Repository // Adds a new source for the user. void AddSource(std::string_view name, std::string_view type, std::string_view arg, IProgressCallback& progress); + struct OpenSourceResult + { + // The ISource returned by OpenSource + std::shared_ptr Source; + + // List of SourceDetails that failed to update + std::vector SourcesWithUpdateFailure; + }; + // Opens an existing source. // Passing an empty string as the name of the source will return a source that aggregates all others. - std::shared_ptr OpenSource(std::string_view name, IProgressCallback& progress); + OpenSourceResult OpenSource(std::string_view name, IProgressCallback& progress); // A predefined source. // These sources are not under the direct control of the user, such as packages installed on the system. diff --git a/src/AppInstallerRepositoryCore/RepositorySource.cpp b/src/AppInstallerRepositoryCore/RepositorySource.cpp index f9eb7693ca..5471d0653a 100644 --- a/src/AppInstallerRepositoryCore/RepositorySource.cpp +++ b/src/AppInstallerRepositoryCore/RepositorySource.cpp @@ -607,7 +607,7 @@ namespace AppInstaller::Repository sourceList.AddSource(details); } - std::shared_ptr OpenSource(std::string_view name, IProgressCallback& progress) + OpenSourceResult OpenSource(std::string_view name, IProgressCallback& progress) { SourceListInternal sourceList; auto currentSources = sourceList.GetCurrentSourceRefs(); @@ -628,6 +628,7 @@ namespace AppInstaller::Repository { AICLI_LOG(Repo, Info, << "Default source requested, multiple sources available, creating aggregated source."); auto aggregatedSource = std::make_shared("*DefaultSource"); + OpenSourceResult result; bool sourceUpdated = false; for (auto& source : currentSources) @@ -636,10 +637,18 @@ namespace AppInstaller::Repository if (ShouldUpdateBeforeOpen(source)) { - // TODO: Consider adding a context callback to indicate we are doing the same action - // to avoid the progress bar fill up multiple times. - UpdateSourceFromDetails(source, progress); - sourceUpdated = true; + try + { + // TODO: Consider adding a context callback to indicate we are doing the same action + // to avoid the progress bar fill up multiple times. + UpdateSourceFromDetails(source, progress); + sourceUpdated = true; + } + catch (...) + { + AICLI_LOG(Repo, Warning, << "Failed to update source: " << source.get().Name); + result.SourcesWithUpdateFailure.emplace_back(source); + } } aggregatedSource->AddAvailableSource(CreateSourceFromDetails(source, progress)); } @@ -649,7 +658,8 @@ namespace AppInstaller::Repository sourceList.SaveMetadata(); } - return aggregatedSource; + result.Source = aggregatedSource; + return result; } } else @@ -664,12 +674,24 @@ namespace AppInstaller::Repository { AICLI_LOG(Repo, Info, << "Named source requested, found: " << source->Name); + OpenSourceResult result; + if (ShouldUpdateBeforeOpen(*source)) { - UpdateSourceFromDetails(*source, progress); - sourceList.SaveMetadata(); + try + { + UpdateSourceFromDetails(*source, progress); + sourceList.SaveMetadata(); + } + catch (...) + { + AICLI_LOG(Repo, Warning, << "Failed to update source: " << (*source).Name); + result.SourcesWithUpdateFailure.emplace_back(*source); + } } - return CreateSourceFromDetails(*source, progress); + + result.Source = CreateSourceFromDetails(*source, progress); + return result; } } } From 4f7ce38037c0392ef856c3c407f216c9a357f55f Mon Sep 17 00:00:00 2001 From: JohnMcPMS Date: Thu, 3 Dec 2020 14:24:25 -0800 Subject: [PATCH 03/47] Fix registry reading code to allow for a single null byte on a wide string value (#662) Move to RegGetValue which normalizes null characters at the end of registry strings --- .github/actions/spelling/expect.txt | 4 +++- src/AppInstallerCLITests/Registry.cpp | 24 ++++++++++++++++++++++++ src/AppInstallerCLITests/TestCommon.cpp | 4 ++-- src/AppInstallerCLITests/TestCommon.h | 2 +- src/AppInstallerCommonCore/Registry.cpp | 2 +- 5 files changed, 31 insertions(+), 5 deletions(-) diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 14473b620d..4d682990aa 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -135,6 +135,7 @@ IHost IID IInstalled incosistencies +INET inl inor iosfwd @@ -207,6 +208,7 @@ mysilentwithprogress mytool Newtonsoft nfinity +NOEXPAND noreturn nuffing nullopt @@ -240,6 +242,7 @@ REGSAM REINSTALLMODE rhs rosoft +RRF rrr rzkzqaqjwj schematab @@ -326,4 +329,3 @@ Wunused WZDNCRFJ xf xl -INET diff --git a/src/AppInstallerCLITests/Registry.cpp b/src/AppInstallerCLITests/Registry.cpp index 7cf1dc1152..ad5f5cf950 100644 --- a/src/AppInstallerCLITests/Registry.cpp +++ b/src/AppInstallerCLITests/Registry.cpp @@ -80,6 +80,30 @@ TEST_CASE("Values_String", "[registry]") REQUIRE(value->GetValue() == ConvertToUTF8(valueValue)); } +TEST_CASE("Values_WideStringWithNarrowNull", "[registry]") +{ + std::wstring valueName = L"TestValueName"; + std::wstring valueValue = L"TestValueValue"; + + wil::unique_hkey root = RegCreateVolatileTestRoot(); + + // Copy the bytes from the string value into a byte vector + std::vector valueBytes; + valueBytes.resize((valueValue.length() + 1) * sizeof(wchar_t)); + memcpy_s(valueBytes.data(), valueBytes.size(), valueValue.c_str(), (valueValue.length() + 1) * sizeof(wchar_t)); + // Remove the last byte to make a narrow null + valueBytes.resize(valueBytes.size() - 1); + + SetRegistryValue(root.get(), valueName, valueBytes, REG_SZ); + + Key key{ root.get(), L"" }; + + auto value = key[valueName]; + REQUIRE(value); + REQUIRE(value->GetType() == Value::Type::String); + REQUIRE(value->GetValue() == ConvertToUTF8(valueValue)); +} + TEST_CASE("Values_ExpandString", "[registry]") { std::wstring valueName = L"TestValueName"; diff --git a/src/AppInstallerCLITests/TestCommon.cpp b/src/AppInstallerCLITests/TestCommon.cpp index 0a0de9dc68..e6ba3b3b00 100644 --- a/src/AppInstallerCLITests/TestCommon.cpp +++ b/src/AppInstallerCLITests/TestCommon.cpp @@ -179,9 +179,9 @@ namespace TestCommon THROW_IF_WIN32_ERROR(RegSetValueExW(key, name.c_str(), 0, type, reinterpret_cast(value.c_str()), static_cast(sizeof(wchar_t) * (value.size() + 1)))); } - void SetRegistryValue(HKEY key, const std::wstring& name, const std::vector& value) + void SetRegistryValue(HKEY key, const std::wstring& name, const std::vector& value, DWORD type) { - THROW_IF_WIN32_ERROR(RegSetValueExW(key, name.c_str(), 0, REG_BINARY, reinterpret_cast(value.data()), static_cast(value.size()))); + THROW_IF_WIN32_ERROR(RegSetValueExW(key, name.c_str(), 0, type, reinterpret_cast(value.data()), static_cast(value.size()))); } void SetRegistryValue(HKEY key, const std::wstring& name, DWORD value) diff --git a/src/AppInstallerCLITests/TestCommon.h b/src/AppInstallerCLITests/TestCommon.h index 2f8314e129..aab515acf3 100644 --- a/src/AppInstallerCLITests/TestCommon.h +++ b/src/AppInstallerCLITests/TestCommon.h @@ -106,7 +106,7 @@ namespace TestCommon // Set registry values. void SetRegistryValue(HKEY key, const std::wstring& name, const std::wstring& value, DWORD type = REG_SZ); - void SetRegistryValue(HKEY key, const std::wstring& name, const std::vector& value); + void SetRegistryValue(HKEY key, const std::wstring& name, const std::vector& value, DWORD type = REG_BINARY); void SetRegistryValue(HKEY key, const std::wstring& name, DWORD value); } diff --git a/src/AppInstallerCommonCore/Registry.cpp b/src/AppInstallerCommonCore/Registry.cpp index cebd2d6009..08eecb2ab3 100644 --- a/src/AppInstallerCommonCore/Registry.cpp +++ b/src/AppInstallerCommonCore/Registry.cpp @@ -231,7 +231,7 @@ namespace AppInstaller::Registry while (data.size() < (64 << 20)) { byteCount = wil::safe_cast(data.size()); - status = RegQueryValueExW(m_key.get(), name.c_str(), nullptr, &type, data.data(), &byteCount); + status = RegGetValueW(m_key.get(), nullptr, name.c_str(), RRF_RT_ANY | RRF_NOEXPAND, &type, data.data(), &byteCount); if (status == ERROR_MORE_DATA && byteCount > data.size()) { From d170d65e75ec91590d3ff9543811f6174af08ce1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Chac=C3=B3n?= Date: Wed, 16 Dec 2020 11:19:39 -0800 Subject: [PATCH 04/47] Implement uninstall flow (#659) Added an UninstallCommand as an experimental feature, and workflow and E2E tests for it. For uninstalling MSIX/MSStore apps this uses the package name, for MSI it uses the product code, and for everything else the uninstall string. This uses already existing information form manifests/ARP. Co-authored-by: Josh Soref --- .github/actions/spelling/allow.txt | 1 + .github/actions/spelling/expect.txt | 6 + .github/actions/spelling/patterns.txt | 2 + doc/Settings.md | 10 ++ doc/settings.schema.json | 5 + .../AppInstallerCLICore.vcxproj | 4 + .../AppInstallerCLICore.vcxproj.filters | 12 ++ .../Commands/RootCommand.cpp | 2 + .../Commands/UninstallCommand.cpp | 135 ++++++++++++++++ .../Commands/UninstallCommand.h | 25 +++ src/AppInstallerCLICore/ExecutionContext.h | 21 +++ src/AppInstallerCLICore/Resources.h | 11 +- .../Workflows/InstallFlow.cpp | 6 +- .../ShellExecuteInstallerHandler.cpp | 108 ++++++++++++- .../Workflows/ShellExecuteInstallerHandler.h | 12 ++ .../Workflows/UninstallFlow.cpp | 126 +++++++++++++++ .../Workflows/UninstallFlow.h | 25 +++ .../Workflows/WorkflowBase.h | 4 +- src/AppInstallerCLIE2ETests/BaseCommand.cs | 7 +- src/AppInstallerCLIE2ETests/Constants.cs | 8 + src/AppInstallerCLIE2ETests/InstallCommand.cs | 9 +- src/AppInstallerCLIE2ETests/SetUpFixture.cs | 2 +- .../Manifests/TestExeInstaller.2.0.0.0.yaml | 1 + .../TestData/Manifests/TestExeInstaller.yaml | 1 + .../TestData/Manifests/TestMsiInstaller.yaml | 1 + .../TestData/Manifests/TestMsixInstaller.yaml | 1 + src/AppInstallerCLIE2ETests/TestIndexSetup.cs | 21 +++ .../UninstallCommand.cs | 145 ++++++++++++++---- .../Shared/Strings/en-us/winget.resw | 22 +++ .../TestData/InstallFlowTest_MSStore.yaml | 1 + .../InstallFlowTest_Msix_DownloadFlow.yaml | 1 + .../InstallFlowTest_Msix_StreamingFlow.yaml | 1 + .../InstallerArgTest_Msi_NoSwitches.yaml | 1 + .../InstallerArgTest_Msi_WithSwitches.yaml | 1 + .../TestData/UpdateFlowTest_Msix.yaml | 1 + src/AppInstallerCLITests/WorkFlow.cpp | 130 +++++++++++++++- .../AppInstallerTelemetry.cpp | 46 ++++-- .../ExperimentalFeature.cpp | 4 + .../Public/AppInstallerErrors.h | 2 + .../Public/AppInstallerTelemetry.h | 3 + .../Public/winget/ExperimentalFeature.h | 3 +- .../Public/winget/UserSettings.h | 2 + src/AppInstallerCommonCore/UserSettings.cpp | 6 + .../Microsoft/ARPHelper.cpp | 14 +- src/AppInstallerTestExeInstaller/main.cpp | 46 +++--- 45 files changed, 911 insertions(+), 84 deletions(-) create mode 100644 src/AppInstallerCLICore/Commands/UninstallCommand.cpp create mode 100644 src/AppInstallerCLICore/Commands/UninstallCommand.h create mode 100644 src/AppInstallerCLICore/Workflows/UninstallFlow.cpp create mode 100644 src/AppInstallerCLICore/Workflows/UninstallFlow.h diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt index 14a3c7a7c4..ac77b33efe 100644 --- a/.github/actions/spelling/allow.txt +++ b/.github/actions/spelling/allow.txt @@ -414,6 +414,7 @@ und undef unicode uninstall +uninstalling Unregister updatemanifest UPLEVEL diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 4d682990aa..a63346a905 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -236,6 +236,7 @@ PWAs PWSTR py pz +qb qword readonly REGSAM @@ -254,6 +255,8 @@ Shlobj sid SIGNATUREHASH Sku +Skype +skypeapp Solaris sortof sourceforge @@ -267,6 +270,7 @@ ssin stackoverflow Standalone startswith +STARTUPINFOW stdarg strchr strcmp @@ -291,6 +295,7 @@ uild uintptr unindent Uninitialize +uninstallation uninstaller uninstallprevious uninstalls @@ -317,6 +322,7 @@ website wesome wiki wikipedia +windir windowsdeveloper winerror wingetdev diff --git a/.github/actions/spelling/patterns.txt b/.github/actions/spelling/patterns.txt index fd8c8599e2..a50481a79e 100644 --- a/.github/actions/spelling/patterns.txt +++ b/.github/actions/spelling/patterns.txt @@ -15,3 +15,5 @@ data:[a-zA-Z=;,/0-9+-]+ \b[0-9a-f]{40}\b \b([A-Za-z])\1{3,}\b El proyecto .* diferentes +# Package family names +\b[-.A-Za-z0-9]+_[a-z0-9]{13}\b diff --git a/doc/Settings.md b/doc/Settings.md index 875cb20d1e..f9ba7858e6 100644 --- a/doc/Settings.md +++ b/doc/Settings.md @@ -87,3 +87,13 @@ While work is in progress on upgrade, the command is hidden behind a feature tog "upgrade": true }, ``` + +### uninstall + +While work is in progress on uninstall, the command is hidden behind a feature toggle. One can enable it as below: + +``` + "experimentalFeatures": { + "uninstall": true + }, +``` diff --git a/doc/settings.schema.json b/doc/settings.schema.json index de30f6685a..45072807b1 100644 --- a/doc/settings.schema.json +++ b/doc/settings.schema.json @@ -57,6 +57,11 @@ "description": "Enable the upgrade command while it is in development", "type": "boolean", "default": false + }, + "uninstall": { + "description": "Enable the uninstall command while it is in development", + "type": "boolean", + "default": false } } } diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj index a4ae59e9c1..6eff93b8ee 100644 --- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj +++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj @@ -185,6 +185,7 @@ + @@ -207,6 +208,7 @@ + @@ -224,6 +226,7 @@ + @@ -244,6 +247,7 @@ + diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters index 20d1285f43..324ea67f93 100644 --- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters +++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters @@ -135,6 +135,12 @@ Commands + + Commands + + + Workflows + @@ -236,6 +242,12 @@ Commands + + Commands + + + Workflows + diff --git a/src/AppInstallerCLICore/Commands/RootCommand.cpp b/src/AppInstallerCLICore/Commands/RootCommand.cpp index b84d79f291..83f2441bc2 100644 --- a/src/AppInstallerCLICore/Commands/RootCommand.cpp +++ b/src/AppInstallerCLICore/Commands/RootCommand.cpp @@ -9,6 +9,7 @@ #include "SearchCommand.h" #include "ListCommand.h" #include "UpgradeCommand.h" +#include "UninstallCommand.h" #include "HashCommand.h" #include "ValidateCommand.h" #include "SettingsCommand.h" @@ -32,6 +33,7 @@ namespace AppInstaller::CLI std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), + std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), std::make_unique(FullName()), diff --git a/src/AppInstallerCLICore/Commands/UninstallCommand.cpp b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp new file mode 100644 index 0000000000..df84be4b78 --- /dev/null +++ b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp @@ -0,0 +1,135 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "UninstallCommand.h" +#include "Workflows/UninstallFlow.h" +#include "Workflows/InstallFlow.h" +#include "Workflows/CompletionFlow.h" +#include "Workflows/WorkflowBase.h" +#include "Resources.h" + +using AppInstaller::CLI::Execution::Args; +using AppInstaller::CLI::Workflow::ExecutionStage; + +namespace AppInstaller::CLI +{ + std::vector UninstallCommand::GetArguments() const + { + // TODO: determine exact arguments needed + return + { + Argument::ForType(Args::Type::Query), + Argument::ForType(Args::Type::Manifest), + Argument::ForType(Args::Type::Id), + Argument::ForType(Args::Type::Name), + Argument::ForType(Args::Type::Moniker), + Argument::ForType(Args::Type::Version), + Argument::ForType(Args::Type::Channel), + Argument::ForType(Args::Type::Source), + Argument::ForType(Args::Type::Exact), + Argument::ForType(Args::Type::Interactive), + Argument::ForType(Args::Type::Silent), + Argument::ForType(Args::Type::Log), + }; + } + + Resource::LocString UninstallCommand::ShortDescription() const + { + return { Resource::String::UninstallCommandShortDescription }; + } + + Resource::LocString UninstallCommand::LongDescription() const + { + return { Resource::String::UninstallCommandLongDescription }; + } + + void UninstallCommand::Complete(Execution::Context& context, Execution::Args::Type valueType) const + { + if (valueType == Execution::Args::Type::Manifest || + valueType == Execution::Args::Type::Log) + { + // Intentionally output nothing to allow pass through to filesystem. + return; + } + + context << + Workflow::OpenSource << + Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed); + + switch (valueType) + { + case Execution::Args::Type::Query: + context << + Workflow::RequireCompletionWordNonEmpty << + Workflow::SearchSourceForManyCompletion << + Workflow::CompleteWithMatchedField; + break; + case Execution::Args::Type::Id: + case Execution::Args::Type::Name: + case Execution::Args::Type::Moniker: + case Execution::Args::Type::Version: + case Execution::Args::Type::Channel: + case Execution::Args::Type::Source: + context << + Workflow::CompleteWithSingleSemanticsForValueUsingExistingSource(valueType); + break; + } + } + + std::string UninstallCommand::HelpLink() const + { + // TODO: point to correct location + return "https://aka.ms/winget-command-uninstall"; + } + + void UninstallCommand::ValidateArgumentsInternal(Execution::Args& execArgs) const + { + if (execArgs.Contains(Execution::Args::Type::Manifest) && + (execArgs.Contains(Execution::Args::Type::Query) || + execArgs.Contains(Execution::Args::Type::Id) || + execArgs.Contains(Execution::Args::Type::Name) || + execArgs.Contains(Execution::Args::Type::Moniker) || + execArgs.Contains(Execution::Args::Type::Version) || + execArgs.Contains(Execution::Args::Type::Channel) || + execArgs.Contains(Execution::Args::Type::Source) || + execArgs.Contains(Execution::Args::Type::Exact))) + { + throw CommandException(Resource::String::BothManifestAndSearchQueryProvided, ""); + } + } + + void UninstallCommand::ExecuteInternal(Execution::Context& context) const + { + // open the sources where to search for the package + context << + Workflow::ReportExecutionStage(ExecutionStage::Discovery) << + Workflow::OpenSource << + Workflow::OpenCompositeSource(Repository::PredefinedSource::Installed); + + // find the uninstaller + if (context.Args.Contains(Execution::Args::Type::Manifest)) + { + // --manifest case where new manifest is provided + context << + Workflow::GetManifestFromArg << + Workflow::ReportManifestIdentity << + Workflow::SearchSourceUsingManifest << + Workflow::EnsureOneMatchFromSearchResult(true); + } + else + { + // search for a single package to uninstall + context << + Workflow::SearchSourceForSingle << + Workflow::EnsureOneMatchFromSearchResult(true) << + Workflow::ReportPackageIdentity; + } + + context << + Workflow::GetInstalledPackageVersion << + Workflow::GetUninstallInfo << + Workflow::ReportExecutionStage(ExecutionStage::Execution) << + Workflow::ExecuteUninstaller << + Workflow::ReportExecutionStage(ExecutionStage::PostExecution); + } +} \ No newline at end of file diff --git a/src/AppInstallerCLICore/Commands/UninstallCommand.h b/src/AppInstallerCLICore/Commands/UninstallCommand.h new file mode 100644 index 0000000000..29ad34246a --- /dev/null +++ b/src/AppInstallerCLICore/Commands/UninstallCommand.h @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "Command.h" + +namespace AppInstaller::CLI +{ + struct UninstallCommand final : public Command + { + UninstallCommand(std::string_view parent) : Command("uninstall", parent, Settings::ExperimentalFeature::Feature::ExperimentalUninstall) {} + + std::vector GetArguments() const override; + + Resource::LocString ShortDescription() const override; + Resource::LocString LongDescription() const override; + + void Complete(Execution::Context& context, Execution::Args::Type valueType) const override; + + std::string HelpLink() const override; + + protected: + void ValidateArgumentsInternal(Execution::Args& execArgs) const override; + void ExecuteInternal(Execution::Context& context) const override; + }; +} diff --git a/src/AppInstallerCLICore/ExecutionContext.h b/src/AppInstallerCLICore/ExecutionContext.h index ac04c51495..71ed0e141f 100644 --- a/src/AppInstallerCLICore/ExecutionContext.h +++ b/src/AppInstallerCLICore/ExecutionContext.h @@ -57,6 +57,9 @@ namespace AppInstaller::CLI::Execution CompletionData, InstalledPackageVersion, ExecutionStage, + UninstallString, + PackageFamilyNames, + ProductCodes, Max }; @@ -163,6 +166,24 @@ namespace AppInstaller::CLI::Execution using value_t = Workflow::ExecutionStage; }; + template <> + struct DataMapping + { + using value_t = std::string; + }; + + template <> + struct DataMapping + { + using value_t = std::vector; + }; + + template <> + struct DataMapping + { + using value_t = std::vector; + }; + // Used to deduce the DataVariant type; making a variant that includes std::monostate and all DataMapping types. template inline auto Deduce(std::index_sequence) { return std::variant(I)>::value_t...>{}; } diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index b23aed7770..095c038566 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -107,13 +107,13 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(MsixArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(MsixSignatureHashFailed); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreAppBlocked); - WINGET_DEFINE_RESOURCE_STRINGID(MSStoreInstallOrUpdateFailed); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreInstallGetEntitlementNetworkError); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreInstallGetEntitlementNoStoreAccount); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreInstallGetEntitlementServerError); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreInstallGetEntitlementSuccess); - WINGET_DEFINE_RESOURCE_STRINGID(MSStoreStoreClientBlocked); + WINGET_DEFINE_RESOURCE_STRINGID(MSStoreInstallOrUpdateFailed); WINGET_DEFINE_RESOURCE_STRINGID(MSStoreInstallTryGetEntitlement); + WINGET_DEFINE_RESOURCE_STRINGID(MSStoreStoreClientBlocked); WINGET_DEFINE_RESOURCE_STRINGID(MultipleInstalledPackagesFound); WINGET_DEFINE_RESOURCE_STRINGID(MultiplePackagesFound); WINGET_DEFINE_RESOURCE_STRINGID(NameArgumentDescription); @@ -121,6 +121,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(NoExperimentalFeaturesMessage); WINGET_DEFINE_RESOURCE_STRINGID(NoInstalledPackageFound); WINGET_DEFINE_RESOURCE_STRINGID(NoPackageFound); + WINGET_DEFINE_RESOURCE_STRINGID(NoUninstallInfoFound); WINGET_DEFINE_RESOURCE_STRINGID(NoVTArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(OpenSourceFailedNoMatch); WINGET_DEFINE_RESOURCE_STRINGID(OpenSourceFailedNoMatchHelp); @@ -205,6 +206,12 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(TooManyArgError); WINGET_DEFINE_RESOURCE_STRINGID(TooManyBehaviorsError); WINGET_DEFINE_RESOURCE_STRINGID(UnexpectedErrorExecutingCommand); + WINGET_DEFINE_RESOURCE_STRINGID(UninstallAbandoned); + WINGET_DEFINE_RESOURCE_STRINGID(UninstallCommandLongDescription); + WINGET_DEFINE_RESOURCE_STRINGID(UninstallCommandShortDescription); + WINGET_DEFINE_RESOURCE_STRINGID(UninstallFailedWithCode); + WINGET_DEFINE_RESOURCE_STRINGID(UninstallFlowUninstallSuccess); + WINGET_DEFINE_RESOURCE_STRINGID(UninstallFlowStartingPackageUninstall); WINGET_DEFINE_RESOURCE_STRINGID(UnrecognizedCommand); WINGET_DEFINE_RESOURCE_STRINGID(UpdateAllArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(UpdateNotApplicable); diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index 48fc577c89..2091529c83 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -2,6 +2,7 @@ // Licensed under the MIT License. #include "pch.h" #include "InstallFlow.h" +#include "UninstallFlow.h" #include "Resources.h" #include "ShellExecuteInstallerHandler.h" #include "MSStoreInstallerHandler.h" @@ -280,9 +281,10 @@ namespace AppInstaller::CLI::Workflow case ManifestInstaller::InstallerTypeEnum::Wix: if (isUpdate && installer.UpdateBehavior == ManifestInstaller::UpdateBehaviorEnum::UninstallPrevious) { - // TODO: hook up with uninstall when uninstall is implemented + context << + GetUninstallInfo << + ExecuteUninstaller; context.ClearFlags(Execution::ContextFlag::InstallerExecutionUseUpdate); - AICLI_TERMINATE_CONTEXT(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); } context << ShellExecuteInstall; break; diff --git a/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp b/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp index 188e370976..f547c915d6 100644 --- a/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp +++ b/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp @@ -7,6 +7,7 @@ using namespace AppInstaller::CLI; using namespace AppInstaller::Utility; using namespace AppInstaller::Manifest; +using namespace AppInstaller::Repository; namespace AppInstaller::CLI::Workflow { @@ -15,7 +16,7 @@ namespace AppInstaller::CLI::Workflow // ShellExecutes the given path. std::optional InvokeShellExecute(const std::filesystem::path& filePath, const std::string& args, IProgressCallback& progress) { - AICLI_LOG(CLI, Info, << "Starting installer: '" << filePath.u8string() << "' with arguments '" << args << '\''); + AICLI_LOG(CLI, Info, << "Starting: '" << filePath.u8string() << "' with arguments '" << args << '\''); SHELLEXECUTEINFOW execInfo = { 0 }; execInfo.cbSize = sizeof(execInfo); @@ -26,10 +27,8 @@ namespace AppInstaller::CLI::Workflow // Some installers force UI. Setting to SW_HIDE will hide installer UI and installation will never complete. // Verified setting to SW_SHOW does not hurt silent mode since no UI will be shown. execInfo.nShow = SW_SHOW; - if (!ShellExecuteExW(&execInfo) || !execInfo.hProcess) - { - return GetLastError(); - } + + THROW_LAST_ERROR_IF(!ShellExecuteExW(&execInfo) || !execInfo.hProcess); wil::unique_process_handle process{ execInfo.hProcess }; @@ -166,6 +165,27 @@ namespace AppInstaller::CLI::Workflow // Todo: language token support will be implemented later } + + // Gets the arguments for uninstalling an MSI with MsiExec + std::string GetMsiExecUninstallArgs(Execution::Context& context, const Utility::LocIndString& productCode) + { + std::string args = "/x" + productCode.get(); + + // Set UI level for MsiExec with the /q flag. + // If interactive is requested, use the default instead of Reduced or Full as the installer may not use them. + if (context.Args.Contains(Execution::Args::Type::Silent)) + { + // n = None = silent + args += " /qn"; + } + else if (!context.Args.Contains(Execution::Args::Type::Interactive)) + { + // b = Basic = only progress bar + args += " /qb"; + } + + return args; + } } void ShellExecuteInstallImpl(Execution::Context& context) @@ -247,4 +267,82 @@ namespace AppInstaller::CLI::Workflow installerPath.assign(renamedDownloadedInstaller); AICLI_LOG(CLI, Info, << "Successfully renamed downloaded installer. Path: " << installerPath); } + + void ShellExecuteUninstallImpl(Execution::Context& context) + { + context.Reporter.Info() << Resource::String::UninstallFlowStartingPackageUninstall << std::endl; + std::wstring commandUtf16 = Utility::ConvertToUTF16(context.Get()); + + // Parse the command string as application and command line for CreateProcess + wil::unique_cotaskmem_string app = nullptr; + wil::unique_cotaskmem_string args = nullptr; + THROW_IF_FAILED(SHEvaluateSystemCommandTemplate(commandUtf16.c_str(), &app, NULL, &args)); + + auto uninstallResult = context.Reporter.ExecuteWithProgress( + std::bind(InvokeShellExecute, + std::filesystem::path(app.get()), + Utility::ConvertToUTF8(args.get()), + std::placeholders::_1)); + + if (!uninstallResult) + { + context.Reporter.Warn() << Resource::String::UninstallAbandoned << std::endl; + AICLI_TERMINATE_CONTEXT(E_ABORT); + } + else if (uninstallResult.value() != 0) + { + const auto installedPackageVersion = context.Get(); + Logging::Telemetry().LogUninstallerFailure( + installedPackageVersion->GetProperty(PackageVersionProperty::Id), + installedPackageVersion->GetProperty(PackageVersionProperty::Version), + "UninstallString", + uninstallResult.value()); + + context.Reporter.Error() << Resource::String::UninstallFailedWithCode << ' ' << uninstallResult.value() << std::endl; + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_EXEC_UNINSTALL_COMMAND_FAILED); + } + else + { + context.Reporter.Info() << Resource::String::UninstallFlowUninstallSuccess << std::endl; + } + } + + void ShellExecuteMsiExecUninstall(Execution::Context& context) + { + const auto& productCodes = context.Get(); + context.Reporter.Info() << Resource::String::UninstallFlowStartingPackageUninstall << std::endl; + + const std::filesystem::path msiexecPath{ ExpandEnvironmentVariables(L"%windir%\\system32\\msiexec.exe") }; + + for (const auto& productCode : productCodes) + { + AICLI_LOG(CLI, Info, << "Removing: " << productCode); + auto uninstallResult = context.Reporter.ExecuteWithProgress( + std::bind(InvokeShellExecute, + msiexecPath, + GetMsiExecUninstallArgs(context, productCode), + std::placeholders::_1)); + + if (!uninstallResult) + { + context.Reporter.Warn() << Resource::String::UninstallAbandoned << std::endl; + AICLI_TERMINATE_CONTEXT(E_ABORT); + } + else if (uninstallResult.value() != 0) + { + // TODO: Check for other success codes + const auto installedPackageVersion = context.Get(); + Logging::Telemetry().LogUninstallerFailure( + installedPackageVersion->GetProperty(PackageVersionProperty::Id), + installedPackageVersion->GetProperty(PackageVersionProperty::Version), + "MsiExec", + uninstallResult.value()); + + context.Reporter.Error() << Resource::String::UninstallFailedWithCode << ' ' << uninstallResult.value() << std::endl; + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_EXEC_UNINSTALL_COMMAND_FAILED); + } + } + + context.Reporter.Info() << Resource::String::UninstallFlowUninstallSuccess << std::endl; + } } \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.h b/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.h index 7d5c6eec5d..227ac22f5f 100644 --- a/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.h +++ b/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.h @@ -17,6 +17,18 @@ namespace AppInstaller::CLI::Workflow // Outputs: None void ShellExecuteInstallImpl(Execution::Context& context); + // Uninstall is done through invoking ShellExecute on uninstall string. + // Required Args: None + // Inputs: UninstallString + // Outputs: None + void ShellExecuteUninstallImpl(Execution::Context& context); + + // Removes the MSI + // Required Args: None + // Inputs: ProductCodes + // Output: None + void ShellExecuteMsiExecUninstall(Execution::Context& context); + // Gets the installer args from the context. // Required Args: None // Inputs: Manifest?, Installer, InstallerPath diff --git a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp new file mode 100644 index 0000000000..ea5ac7f70f --- /dev/null +++ b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp @@ -0,0 +1,126 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "pch.h" +#include "UninstallFlow.h" +#include "WorkflowBase.h" +#include "ShellExecuteInstallerHandler.h" +#include "AppInstallerMsixInfo.h" + +using namespace AppInstaller::Manifest; +using namespace AppInstaller::Msix; +using namespace AppInstaller::Repository; + +namespace AppInstaller::CLI::Workflow +{ + void GetUninstallInfo(Execution::Context& context) + { + auto installedPackageVersion = context.Get(); + const std::string installedTypeString = installedPackageVersion->GetMetadata()[PackageVersionMetadata::InstalledType]; + switch (ManifestInstaller::ConvertToInstallerTypeEnum(installedTypeString)) + { + case ManifestInstaller::InstallerTypeEnum::Exe: + case ManifestInstaller::InstallerTypeEnum::Burn: + case ManifestInstaller::InstallerTypeEnum::Inno: + case ManifestInstaller::InstallerTypeEnum::Nullsoft: + { + IPackageVersion::Metadata packageMetadata = installedPackageVersion->GetMetadata(); + + // Default to silent unless it is not present or interactivity is requested + auto uninstallCommandItr = packageMetadata.find(PackageVersionMetadata::SilentUninstallCommand); + if (uninstallCommandItr == packageMetadata.end() || context.Args.Contains(Execution::Args::Type::Interactive)) + { + auto interactiveItr = packageMetadata.find(PackageVersionMetadata::StandardUninstallCommand); + if (interactiveItr != packageMetadata.end()) + { + uninstallCommandItr = interactiveItr; + } + } + + if (uninstallCommandItr == packageMetadata.end()) + { + context.Reporter.Error() << Resource::String::NoUninstallInfoFound << std::endl; + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NO_UNINSTALL_INFO_FOUND); + } + + context.Add(uninstallCommandItr->second); + break; + } + case ManifestInstaller::InstallerTypeEnum::Msi: + case ManifestInstaller::InstallerTypeEnum::Wix: + { + // Uninstall strings for MSI don't include UI level (/q) needed to avoid interactivity, + // so we handle them differently. + auto productCodes = installedPackageVersion->GetMultiProperty(PackageVersionMultiProperty::ProductCode); + if (productCodes.empty()) + { + context.Reporter.Error() << Resource::String::NoUninstallInfoFound << std::endl; + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NO_UNINSTALL_INFO_FOUND); + } + + context.Add(std::move(productCodes)); + break; + } + case ManifestInstaller::InstallerTypeEnum::Msix: + case ManifestInstaller::InstallerTypeEnum::MSStore: + { + auto packageFamilyNames = installedPackageVersion->GetMultiProperty(PackageVersionMultiProperty::PackageFamilyName); + if (packageFamilyNames.empty()) + { + context.Reporter.Error() << Resource::String::NoUninstallInfoFound << std::endl; + AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NO_UNINSTALL_INFO_FOUND); + } + + context.Add(packageFamilyNames); + break; + } + default: + THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); + } + } + + void ExecuteUninstaller(Execution::Context& context) + { + const std::string installedTypeString = context.Get()->GetMetadata()[PackageVersionMetadata::InstalledType]; + switch (ManifestInstaller::ConvertToInstallerTypeEnum(installedTypeString)) + { + case ManifestInstaller::InstallerTypeEnum::Exe: + case ManifestInstaller::InstallerTypeEnum::Burn: + case ManifestInstaller::InstallerTypeEnum::Inno: + case ManifestInstaller::InstallerTypeEnum::Nullsoft: + context << Workflow::ShellExecuteUninstallImpl; + break; + case ManifestInstaller::InstallerTypeEnum::Msi: + case ManifestInstaller::InstallerTypeEnum::Wix: + context << Workflow::ShellExecuteMsiExecUninstall; + break; + case ManifestInstaller::InstallerTypeEnum::Msix: + case ManifestInstaller::InstallerTypeEnum::MSStore: + context << Workflow::MsixUninstall; + break; + default: + THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); + } + } + + void MsixUninstall(Execution::Context& context) + { + const auto& packageFamilyNames = context.Get(); + context.Reporter.Info() << Resource::String::UninstallFlowStartingPackageUninstall << std::endl; + + for (const auto& packageFamilyName : packageFamilyNames) + { + auto packageFullName = Msix::GetPackageFullNameFromFamilyName(packageFamilyName); + if (!packageFullName.has_value()) + { + AICLI_LOG(CLI, Warning, << "No package found with family name: " << packageFamilyName); + continue; + } + + AICLI_LOG(CLI, Info, << "Removing MSIX package: " << packageFullName.value()); + context.Reporter.ExecuteWithProgress(std::bind(Deployment::RemovePackage, packageFullName.value(), std::placeholders::_1)); + } + + context.Reporter.Info() << Resource::String::UninstallFlowUninstallSuccess << std::endl; + } +} \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/UninstallFlow.h b/src/AppInstallerCLICore/Workflows/UninstallFlow.h new file mode 100644 index 0000000000..8a71a23040 --- /dev/null +++ b/src/AppInstallerCLICore/Workflows/UninstallFlow.h @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "ExecutionContext.h" + +namespace AppInstaller::CLI::Workflow +{ + // Gets the command string or package family names used to uninstall the package. + // Required Args: None + // Inputs: InstalledPackageVersion + // Output: UninstallString?, PackageFamilyNames? + void GetUninstallInfo(Execution::Context& context); + + // Uninstalls the package according to its type. + // Required Args: None + // Inputs: InstalledPackageVersion, UninstallString?, PackageFamilyNames? + // Output: None + void ExecuteUninstaller(Execution::Context& context); + + // Removes the MSIX. + // Required Args: None + // Inputs: PackageFamilyNames + // Outputs: None + void MsixUninstall(Execution::Context& context); +} \ No newline at end of file diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.h b/src/AppInstallerCLICore/Workflows/WorkflowBase.h index add4ec792b..8f457c2132 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.h +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.h @@ -155,7 +155,7 @@ namespace AppInstaller::CLI::Workflow void ReportMultiplePackageFoundResult(Execution::Context& context); // Ensures that there is at least one result in the search. - // Required Args: bool indicating id the search result is from installed source + // Required Args: bool indicating if the search result is from installed source // Inputs: SearchResult // Outputs: None struct EnsureMatchesFromSearchResult : public WorkflowTask @@ -170,7 +170,7 @@ namespace AppInstaller::CLI::Workflow }; // Ensures that there is only one result in the search. - // Required Args: bool indicating id the search result is from installed source + // Required Args: bool indicating if the search result is from installed source // Inputs: SearchResult // Outputs: None struct EnsureOneMatchFromSearchResult : public WorkflowTask diff --git a/src/AppInstallerCLIE2ETests/BaseCommand.cs b/src/AppInstallerCLIE2ETests/BaseCommand.cs index a1f90ecf28..77c284701a 100644 --- a/src/AppInstallerCLIE2ETests/BaseCommand.cs +++ b/src/AppInstallerCLIE2ETests/BaseCommand.cs @@ -12,7 +12,9 @@ namespace AppInstallerCLIE2ETests public class BaseCommand { - public readonly string SettingsJsonFilePath = @"Packages\WinGetDevCLI_8wekyb3d8bbwe\LocalState\settings.json"; + public string SettingsJsonFilePath => TestCommon.PackagedContext ? + @"Packages\WinGetDevCLI_8wekyb3d8bbwe\LocalState\settings.json" : + @"Microsoft\WinGet\Settings\settings.json"; public readonly string LocalAppData = "LocalAppData"; [OneTimeSetUp] @@ -57,7 +59,8 @@ public void InitializeAllFeatures(bool status) experimentalCmd = status, experimentalMSStore = status, list = status, - upgrade = status + upgrade = status, + uninstall = status, } }; diff --git a/src/AppInstallerCLIE2ETests/Constants.cs b/src/AppInstallerCLIE2ETests/Constants.cs index 21bd8ff245..abce1b5929 100644 --- a/src/AppInstallerCLIE2ETests/Constants.cs +++ b/src/AppInstallerCLIE2ETests/Constants.cs @@ -45,6 +45,14 @@ public class Constants public const string WinGetUtil = "WinGetUtil"; public const string E2ETestLogsPath = @"Packages\WinGetDevCLI_8wekyb3d8bbwe\LocalState\DiagOutputDir"; + // Test installers' package IDs + public const string ExeInstallerPackageId = "AppInstallerTest.TestExeInstaller"; + public const string MsiInstallerPackageId = "AppInstallerTest.TestMsiInstaller"; + public const string MsixInstallerPackageId = "AppInstallerTest.TestMsixInstaller"; + + public const string MsiInstallerProductCode = "{A5D36CF1-1993-4F63-BFB4-3ACD910D36A1}"; + public const string MsixInstallerPackageFamilyName = "6c6338fe-41b7-46ca-8ba6-b5ad5312bb0e_8wekyb3d8bbwe"; + public class ErrorCode { public const int S_OK = 0; diff --git a/src/AppInstallerCLIE2ETests/InstallCommand.cs b/src/AppInstallerCLIE2ETests/InstallCommand.cs index 17c22ca103..99630b2812 100644 --- a/src/AppInstallerCLIE2ETests/InstallCommand.cs +++ b/src/AppInstallerCLIE2ETests/InstallCommand.cs @@ -9,7 +9,7 @@ namespace AppInstallerCLIE2ETests public class InstallCommand : BaseCommand { private const string InstallTestExeInstalledFile = @"TestExeInstalled.txt"; - private const string InstallTestMsiInstalledFile = @"AppInstallerTestMsiInstaller.msi"; + private const string InstallTestMsiInstalledFile = @"AppInstallerTestExeInstaller.exe"; private const string InstallTestMsiProductId = @"{A5D36CF1-1993-4F63-BFB4-3ACD910D36A1}"; private const string InstallTestMsixName = @"6c6338fe-41b7-46ca-8ba6-b5ad5312bb0e"; @@ -92,9 +92,14 @@ public void InstallNullSoft() Assert.True(VerifyTestExeInstalled(installDir, "/S")); } - //[Test] + [Test] public void InstallMSI() { + if (string.IsNullOrEmpty(TestCommon.MsiInstallerPath)) + { + Assert.Ignore("MSI installer not available"); + } + var installDir = TestCommon.GetRandomTestDir(); var result = TestCommon.RunAICLICommand("install", $"TestMsiInstaller --silent -l {installDir}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); diff --git a/src/AppInstallerCLIE2ETests/SetUpFixture.cs b/src/AppInstallerCLIE2ETests/SetUpFixture.cs index 39c3c234f8..9c20bbb1c0 100644 --- a/src/AppInstallerCLIE2ETests/SetUpFixture.cs +++ b/src/AppInstallerCLIE2ETests/SetUpFixture.cs @@ -64,7 +64,7 @@ public void Setup() ShouldDisableDevModeOnExit = EnableDevMode(true); - ShouldRevertDefaultFileTypeRiskOnExit = DecreaseFileTypeRisk(".exe", false); + ShouldRevertDefaultFileTypeRiskOnExit = DecreaseFileTypeRisk(".exe;.msi", false); Assert.True(TestCommon.RunCommand("certutil.exe", "-addstore -f \"TRUSTEDPEOPLE\" " + TestCommon.GetTestDataFile(Constants.AppInstallerTestCert)), "Add AppInstallerTestCert"); Assert.True(TestCommon.RunCommand("certutil.exe", "-addstore -f \"ROOT\" " + TestCommon.GetTestDataFile(Constants.IndexPackageRootCert)), "Add IndexPackageRootCert"); diff --git a/src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller.2.0.0.0.yaml b/src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller.2.0.0.0.yaml index c17eef567e..1a6e9d673c 100644 --- a/src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller.2.0.0.0.yaml +++ b/src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller.2.0.0.0.yaml @@ -8,6 +8,7 @@ Installers: Url: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe Sha256: InstallerType: exe + ProductCode: '{A499DD5E-8DC5-4AD2-911A-BCD0263295E9}' Switches: Custom: /execustom SilentWithProgress: /exeswp diff --git a/src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller.yaml b/src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller.yaml index 60bb28f482..32e6a73c55 100644 --- a/src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller.yaml +++ b/src/AppInstallerCLIE2ETests/TestData/Manifests/TestExeInstaller.yaml @@ -8,6 +8,7 @@ Installers: Url: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe Sha256: InstallerType: exe + ProductCode: '{A499DD5E-8DC5-4AD2-911A-BCD0263295E9}' Switches: Custom: /execustom SilentWithProgress: /exeswp diff --git a/src/AppInstallerCLIE2ETests/TestData/Manifests/TestMsiInstaller.yaml b/src/AppInstallerCLIE2ETests/TestData/Manifests/TestMsiInstaller.yaml index 8d2876825a..6133191be3 100644 --- a/src/AppInstallerCLIE2ETests/TestData/Manifests/TestMsiInstaller.yaml +++ b/src/AppInstallerCLIE2ETests/TestData/Manifests/TestMsiInstaller.yaml @@ -8,4 +8,5 @@ Installers: Url: https://localhost:5001/TestKit/AppInstallerTestMsiInstaller/AppInstallerTestMsiInstaller.msi Sha256: InstallerType: msi + ProductCode: '{A5D36CF1-1993-4F63-BFB4-3ACD910D36A1}' ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLIE2ETests/TestData/Manifests/TestMsixInstaller.yaml b/src/AppInstallerCLIE2ETests/TestData/Manifests/TestMsixInstaller.yaml index f1f9fccdf8..932a2a5de9 100644 --- a/src/AppInstallerCLIE2ETests/TestData/Manifests/TestMsixInstaller.yaml +++ b/src/AppInstallerCLIE2ETests/TestData/Manifests/TestMsixInstaller.yaml @@ -8,4 +8,5 @@ Installers: Url: https://localhost:5001/TestKit/AppInstallerTestMsixInstaller/AppInstallerTestMsixInstaller.msix Sha256: InstallerType: msix + PackageFamilyName: 6c6338fe-41b7-46ca-8ba6-b5ad5312bb0e_8wekyb3d8bbwe ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLIE2ETests/TestIndexSetup.cs b/src/AppInstallerCLIE2ETests/TestIndexSetup.cs index 02c5773616..d74a69cc96 100644 --- a/src/AppInstallerCLIE2ETests/TestIndexSetup.cs +++ b/src/AppInstallerCLIE2ETests/TestIndexSetup.cs @@ -31,6 +31,11 @@ public static void GenerateTestDirectory() CopyExeInstallerToTestDirectory(); } + if (!string.IsNullOrEmpty(TestCommon.MsiInstallerPath)) + { + CopyMsiInstallerToTestDirectory(); + } + if (!string.IsNullOrEmpty(TestCommon.MsixInstallerPath)) { CopyMsixInstallerToTestDirectory(); @@ -95,6 +100,22 @@ private static void CopyExeInstallerToTestDirectory() SignFile(TestCommon.ExeInstallerPath); } + private static void CopyMsiInstallerToTestDirectory() + { + // Set MSI Test Installer Path + string msiInstallerDestPath = Path.Combine(TestCommon.StaticFileRootPath, Constants.MsiInstaller); + DirectoryInfo msiInstallerDestDir = Directory.CreateDirectory(msiInstallerDestPath); + + // Copy MSI Test Installer to Destination Path + string msiInstallerFullName = Path.Combine(msiInstallerDestDir.FullName, "AppInstallerTestMsiInstaller.msi"); + + File.Copy(TestCommon.MsiInstallerPath, msiInstallerFullName, true); + TestCommon.MsiInstallerPath = msiInstallerFullName; + + // Sign MSI Installer File + SignFile(TestCommon.MsiInstallerPath); + } + private static void CopyMsixInstallerToTestDirectory() { // Set Msix Test Installer Path diff --git a/src/AppInstallerCLIE2ETests/UninstallCommand.cs b/src/AppInstallerCLIE2ETests/UninstallCommand.cs index e102a51bf2..37fb4f4e43 100644 --- a/src/AppInstallerCLIE2ETests/UninstallCommand.cs +++ b/src/AppInstallerCLIE2ETests/UninstallCommand.cs @@ -1,30 +1,115 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -namespace AppInstallerCLIE2ETests -{ - using NUnit.Framework; - using System.IO; - - public class UninstallCommand : BaseCommand - { - private const string UninstallTestExeInstalledFile = @"TestExeUninstalled.txt"; - - //[Test] - public void UninstallTestExe() - { - // Example Uninstall Command Test - var installDir = TestCommon.GetRandomTestDir(); - TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestExeInstaller --silent -l {installDir}"); - var result = TestCommon.RunAICLICommand("uninstall", $"AppInstallerTest.TextExeInstaller"); - Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); - Assert.True(result.StdOut.Contains("Successfully uninstalled")); - Assert.True(VerifyTestExeUninstalled(installDir)); - } - - private bool VerifyTestExeUninstalled(string uninstallDir) - { - return File.Exists(Path.Combine(uninstallDir, UninstallTestExeInstalledFile)); - } - } -} +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace AppInstallerCLIE2ETests +{ + using NUnit.Framework; + using System.IO; + + public class UninstallCommand : BaseCommand + { + // Custom product code for overriding the default in the test exe + private const string CustomProductCode = "{f08fc03c-0b7e-4fca-9b3c-3a384d18a9f3}"; + + // File written when uninstalling the test exe + private const string UninstallTestExeUninstalledFile = "TestExeUninstalled.txt"; + + // Name of a file installed by the MSI that will be removed during uninstall + private const string UninstallTestMsiInstalledFile = "AppInstallerTestExeInstaller.exe"; + + // Package name of the test MSIX package + private const string UninstallTestMsixName = "6c6338fe-41b7-46ca-8ba6-b5ad5312bb0e"; + + [OneTimeSetUp] + public void OneTimeSetUp() + { + InitializeAllFeatures(false); + ConfigureFeature("uninstall", true); + } + + [OneTimeTearDown] + public void OneTimeTearDown() + { + InitializeAllFeatures(false); + } + + [Test] + public void UninstallTestExe() + { + // Uninstall an Exe + var installDir = TestCommon.GetRandomTestDir(); + TestCommon.RunAICLICommand("install", $"{Constants.ExeInstallerPackageId} --silent -l {installDir}"); + var result = TestCommon.RunAICLICommand("uninstall", Constants.ExeInstallerPackageId); + Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); + Assert.True(result.StdOut.Contains("Successfully uninstalled")); + Assert.True(VerifyTestExeUninstalled(installDir)); + } + + [Test] + public void UninstallTestMsi() + { + if (string.IsNullOrEmpty(TestCommon.MsiInstallerPath)) + { + Assert.Ignore("MSI installer not available"); + } + + // Uninstall an MSI + var installDir = TestCommon.GetRandomTestDir(); + TestCommon.RunAICLICommand("install", $"{Constants.MsiInstallerPackageId} -l {installDir}"); + var result = TestCommon.RunAICLICommand("uninstall", Constants.MsiInstallerPackageId); + Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); + Assert.True(result.StdOut.Contains("Successfully uninstalled")); + Assert.True(VerifyTestMsiUninstalled(installDir)); + } + + [Test] + public void UninstallTestMsix() + { + // Uninstall an MSIX + TestCommon.RunAICLICommand("install", Constants.MsixInstallerPackageId); + var result = TestCommon.RunAICLICommand("uninstall", Constants.MsixInstallerPackageId); + Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); + Assert.True(result.StdOut.Contains("Successfully uninstalled")); + Assert.True(VerifyTestMsixUninstalled()); + } + + [Test] + public void UninstallNotIndexed() + { + // Uninstalls a package found with ARP not matching any known manifest. + // Install the test EXE providing a custom Product Code so that it cannot be mapped + // back to its manifest, then uninstall it using its Product Code + var installDir = TestCommon.GetRandomTestDir(); + TestCommon.RunAICLICommand("install", $"{Constants.ExeInstallerPackageId} --override \"/ProductID {CustomProductCode} /InstallDir {installDir}"); + var result = TestCommon.RunAICLICommand("uninstall", CustomProductCode); + Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); + Assert.True(result.StdOut.Contains("Successfully uninstalled")); + Assert.True(VerifyTestExeUninstalled(installDir)); + } + + [Test] + public void UninstallAppNotInstalled() + { + // Verify failure when trying to uninstall an app that is not installed. + var result = TestCommon.RunAICLICommand("uninstall", $"TestMsixInstaller"); + Assert.AreEqual(Constants.ErrorCode.ERROR_NO_APPLICATIONS_FOUND, result.ExitCode); + Assert.True(result.StdOut.Contains("No installed package found matching input criteria.")); + } + + private bool VerifyTestExeUninstalled(string installDir) + { + return File.Exists(Path.Combine(installDir, UninstallTestExeUninstalledFile)); + } + + private bool VerifyTestMsiUninstalled(string installDir) + { + return !File.Exists(Path.Combine(installDir, UninstallTestMsiInstalledFile)); + } + + private bool VerifyTestMsixUninstalled() + { + var result = TestCommon.RunCommandWithResult("powershell", $"Get-AppxPackage {UninstallTestMsixName}"); + return string.IsNullOrWhiteSpace(result.StdOut); + } + } +} diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 7e685f343d..456fae16e0 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -734,4 +734,26 @@ They can be configured through the settings file 'winget settings'. Failed in attempting to update the source: + + Uninstalls the selected package, either found by searching the installed packages list or directly from a manifest. By default, the query must case-insensitively match the id, name, or moniker of the package. Other fields can be used by passing their appropriate option. + + + Uninstalls the given package + + + Starting package uninstall... + + + Successfully uninstalled + + + winget cannot locate the uninstall command for this package. Please reach out to the package publisher for support. + {Locked="winget"} + + + Uninstallation abandoned + + + Uninstall failed with exit code: + \ No newline at end of file diff --git a/src/AppInstallerCLITests/TestData/InstallFlowTest_MSStore.yaml b/src/AppInstallerCLITests/TestData/InstallFlowTest_MSStore.yaml index 090a8ed85c..abf1ddc81d 100644 --- a/src/AppInstallerCLITests/TestData/InstallFlowTest_MSStore.yaml +++ b/src/AppInstallerCLITests/TestData/InstallFlowTest_MSStore.yaml @@ -9,4 +9,5 @@ Installers: Url: https://ThisIsNotUsed InstallerType: MSStore ProductId: 9WZDNCRFJ364 + PackageFamilyName: Microsoft.SkypeApp_kzf8qxf38zg5c ManifestVersion: 0.2.0-msstore diff --git a/src/AppInstallerCLITests/TestData/InstallFlowTest_Msix_DownloadFlow.yaml b/src/AppInstallerCLITests/TestData/InstallFlowTest_Msix_DownloadFlow.yaml index a2bf26d00c..23a9996ebf 100644 --- a/src/AppInstallerCLITests/TestData/InstallFlowTest_Msix_DownloadFlow.yaml +++ b/src/AppInstallerCLITests/TestData/InstallFlowTest_Msix_DownloadFlow.yaml @@ -9,4 +9,5 @@ Installers: Url: https://github.com/microsoft/msix-packaging/blob/master/src/test/testData/unpack/TestAppxPackage_x64.appx?raw=true InstallerType: msix Sha256: 6a2d3683fa19bf00e58e07d1313d20a5f5735ebbd6a999d33381d28740ee07ea + PackageFamilyName: 20477fca-282d-49fb-b03e-371dca074f0f_8wekyb3d8bbwe ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/InstallFlowTest_Msix_StreamingFlow.yaml b/src/AppInstallerCLITests/TestData/InstallFlowTest_Msix_StreamingFlow.yaml index 36f158c36c..b9ada47afd 100644 --- a/src/AppInstallerCLITests/TestData/InstallFlowTest_Msix_StreamingFlow.yaml +++ b/src/AppInstallerCLITests/TestData/InstallFlowTest_Msix_StreamingFlow.yaml @@ -10,4 +10,5 @@ Installers: InstallerType: msix Sha256: 6a2d3683fa19bf00e58e07d1313d20a5f5735ebbd6a999d33381d28740ee07ea SignatureSha256: 138781c3e6f635240353f3d14d1d57bdcb89413e49be63b375e6a5d7b93b0d07 + PackageFamilyName: 20477fca-282d-49fb-b03e-371dca074f0f_8wekyb3d8bbwe ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/InstallerArgTest_Msi_NoSwitches.yaml b/src/AppInstallerCLITests/TestData/InstallerArgTest_Msi_NoSwitches.yaml index ccf36be366..ca0bc77c93 100644 --- a/src/AppInstallerCLITests/TestData/InstallerArgTest_Msi_NoSwitches.yaml +++ b/src/AppInstallerCLITests/TestData/InstallerArgTest_Msi_NoSwitches.yaml @@ -9,4 +9,5 @@ Installers: Url: https://ThisIsNotUsed InstallerType: msi Sha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B + ProductCode: '{A5D36CF1-1993-4F63-BFB4-3ACD910D36A1}' ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/TestData/InstallerArgTest_Msi_WithSwitches.yaml b/src/AppInstallerCLITests/TestData/InstallerArgTest_Msi_WithSwitches.yaml index e311400003..a7e4630b39 100644 --- a/src/AppInstallerCLITests/TestData/InstallerArgTest_Msi_WithSwitches.yaml +++ b/src/AppInstallerCLITests/TestData/InstallerArgTest_Msi_WithSwitches.yaml @@ -9,6 +9,7 @@ Installers: Url: https://ThisIsNotUsed InstallerType: msi Sha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B + ProductCode: '{A5D36CF1-1993-4F63-BFB4-3ACD910D36A1}' Switches: Custom: /mycustom SilentWithProgress: /mysilentwithprogress diff --git a/src/AppInstallerCLITests/TestData/UpdateFlowTest_Msix.yaml b/src/AppInstallerCLITests/TestData/UpdateFlowTest_Msix.yaml index 46b7cc7c13..8fa39c20b3 100644 --- a/src/AppInstallerCLITests/TestData/UpdateFlowTest_Msix.yaml +++ b/src/AppInstallerCLITests/TestData/UpdateFlowTest_Msix.yaml @@ -11,4 +11,5 @@ Installers: InstallerType: msix Sha256: 6a2d3683fa19bf00e58e07d1313d20a5f5735ebbd6a999d33381d28740ee07ea SignatureSha256: 138781c3e6f635240353f3d14d1d57bdcb89413e49be63b375e6a5d7b93b0d07 + PackageFamilyName: 20477fca-282d-49fb-b03e-371dca074f0f_8wekyb3d8bbwe ManifestVersion: 0.1.0 diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index d5104d5a82..e236c3eba8 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -17,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -111,7 +113,12 @@ namespace ResultMatch( TestPackage::Make( manifest, - TestPackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Exe" } }, + TestPackage::MetadataMap + { + { PackageVersionMetadata::InstalledType, "Exe" }, + { PackageVersionMetadata::StandardUninstallCommand, "C:\\uninstall.exe" }, + { PackageVersionMetadata::SilentUninstallCommand, "C:\\uninstall.exe /silence" }, + }, std::vector{ manifest2, manifest } ), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeInstaller"))); @@ -309,6 +316,19 @@ void OverrideForShellExecute(TestContext& context) OverrideForUpdateInstallerMotw(context); } +void OverrideForExeUninstall(TestContext& context) +{ + context.Override({ ShellExecuteUninstallImpl, [](TestContext& context) + { + // Write out the uninstall command + std::filesystem::path temp = std::filesystem::temp_directory_path(); + temp /= "TestExeUninstalled.txt"; + std::ofstream file(temp, std::ofstream::out); + file << context.Get(); + file.close(); + } }); +} + void OverrideForMSIX(TestContext& context) { context.Override({ MsixInstall, [](TestContext& context) @@ -330,6 +350,23 @@ void OverrideForMSIX(TestContext& context) } }); } +void OverrideForMSIXUninstall(TestContext& context) +{ + context.Override({ MsixUninstall, [](TestContext& context) + { + // Write out the package full name + std::filesystem::path temp = std::filesystem::temp_directory_path(); + temp /= "TestMsixUninstalled.txt"; + std::ofstream file(temp, std::ofstream::out); + for (const auto& packageFamilyName : context.Get()) + { + file << packageFamilyName << std::endl; + } + + file.close(); + } }); +} + void OverrideForMSStore(TestContext& context, bool isUpdate) { if (isUpdate) @@ -940,6 +977,97 @@ TEST_CASE("UpdateFlow_UpdateAllApplicable", "[UpdateFlow][workflow]") REQUIRE(std::filesystem::exists(updateMSStoreResultPath.GetPath())); } +TEST_CASE("UninstallFlow_UninstallExe", "[UninstallFlow][workflow]") +{ + TestCommon::TempFile uninstallResultPath("TestExeUninstalled.txt"); + + std::ostringstream uninstallOutput; + TestContext context{ uninstallOutput, std::cin }; + OverrideForCompositeInstalledSource(context); + OverrideForExeUninstall(context); + context.Args.AddArg(Execution::Args::Type::Query, "AppInstallerCliTest.TestExeInstaller"sv); + context.Args.AddArg(Execution::Args::Type::Silent); + + UninstallCommand uninstall({}); + uninstall.Execute(context); + INFO(uninstallOutput.str()); + + // Verify Uninstaller is called and parameters are passed in. + REQUIRE(std::filesystem::exists(uninstallResultPath.GetPath())); + std::ifstream uninstallResultFile(uninstallResultPath.GetPath()); + REQUIRE(uninstallResultFile.is_open()); + std::string uninstallResultStr; + std::getline(uninstallResultFile, uninstallResultStr); + REQUIRE(uninstallResultStr.find("uninstall.exe") != std::string::npos); + REQUIRE(uninstallResultStr.find("/silence") != std::string::npos); +} + +TEST_CASE("UninstallFlow_UninstallMsix", "[UninstallFlow][workflow]") +{ + TestCommon::TempFile uninstallResultPath("TestMsixUninstalled.txt"); + + std::ostringstream uninstallOutput; + TestContext context{ uninstallOutput, std::cin }; + OverrideForCompositeInstalledSource(context); + OverrideForMSIXUninstall(context); + context.Args.AddArg(Execution::Args::Type::Query, "AppInstallerCliTest.TestMsixInstaller"sv); + + UninstallCommand uninstall({}); + uninstall.Execute(context); + INFO(uninstallOutput.str()); + + // Verify Uninstaller is called with the package full name. + REQUIRE(std::filesystem::exists(uninstallResultPath.GetPath())); + std::ifstream uninstallResultFile(uninstallResultPath.GetPath()); + REQUIRE(uninstallResultFile.is_open()); + std::string uninstallResultStr; + std::getline(uninstallResultFile, uninstallResultStr); + REQUIRE(uninstallResultStr.find("20477fca-282d-49fb-b03e-371dca074f0f_8wekyb3d8bbwe") != std::string::npos); +} + +TEST_CASE("UninstallFlow_UninstallMSStore", "[UninstallFlow][workflow]") +{ + TestCommon::TempFile uninstallResultPath("TestMsixUninstalled.txt"); + + std::ostringstream uninstallOutput; + TestContext context{ uninstallOutput, std::cin }; + OverrideForCompositeInstalledSource(context); + OverrideForMSIXUninstall(context); + context.Args.AddArg(Execution::Args::Type::Query, "AppInstallerCliTest.TestMSStoreInstaller"sv); + + UninstallCommand uninstall({}); + uninstall.Execute(context); + INFO(uninstallOutput.str()); + + // Verify Uninstaller is called with the package full name + REQUIRE(std::filesystem::exists(uninstallResultPath.GetPath())); + std::ifstream uninstallResultFile(uninstallResultPath.GetPath()); + REQUIRE(uninstallResultFile.is_open()); + std::string uninstallResultStr; + std::getline(uninstallResultFile, uninstallResultStr); + REQUIRE(uninstallResultStr.find("microsoft.skypeapp_kzf8qxf38zg5c") != std::string::npos); +} + +TEST_CASE("UninstallFlow_UninstallExeNotFound", "[UninstallFlow][workflow]") +{ + TestCommon::TempFile uninstallResultPath("TestExeUninstalled.txt"); + + std::ostringstream uninstallOutput; + TestContext context{ uninstallOutput, std::cin }; + OverrideForCompositeInstalledSource(context); + context.Args.AddArg(Execution::Args::Type::Query, "AppInstallerCliTest.MissingApp"sv); + context.Args.AddArg(Execution::Args::Type::Silent); + + UninstallCommand uninstall({}); + uninstall.Execute(context); + INFO(uninstallOutput.str()); + + // Verify Uninstaller is not called. + REQUIRE(!std::filesystem::exists(uninstallResultPath.GetPath())); + REQUIRE(uninstallOutput.str().find(Resource::LocString(Resource::String::NoInstalledPackageFound).get()) != std::string::npos); + REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_NO_APPLICATIONS_FOUND); +} + void VerifyMotw(const std::filesystem::path& testFile, DWORD zone) { std::filesystem::path motwFile(testFile); diff --git a/src/AppInstallerCommonCore/AppInstallerTelemetry.cpp b/src/AppInstallerCommonCore/AppInstallerTelemetry.cpp index adbb26997a..36cc6d2bfd 100644 --- a/src/AppInstallerCommonCore/AppInstallerTelemetry.cpp +++ b/src/AppInstallerCommonCore/AppInstallerTelemetry.cpp @@ -421,8 +421,28 @@ namespace AppInstaller::Logging } AICLI_LOG(CLI, Error, << type << " installer failed: " << errorCode); - } - + } + + void TelemetryTraceLogger::LogUninstallerFailure(std::string_view id, std::string_view version, std::string_view type, uint32_t errorCode) + { + if (IsTelemetryEnabled()) + { + TraceLoggingWriteActivity(g_hTelemetryProvider, + "UninstallerFailure", + GetActivityId(), + nullptr, + TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), + AICLI_TraceLoggingStringView(id, "Id"), + AICLI_TraceLoggingStringView(version, "Version"), + AICLI_TraceLoggingStringView(type, "Type"), + TraceLoggingUInt32(errorCode, "ErrorCode"), + TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance | PDT_ProductAndServiceUsage), + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); + } + + AICLI_LOG(CLI, Error, << type << " uninstaller failed: " << errorCode); + } + void TelemetryTraceLogger::LogDuplicateARPEntry(HRESULT hr, std::string_view scope, std::string_view architecture, std::string_view productCode, std::string_view name) { if (IsTelemetryEnabled()) @@ -469,15 +489,15 @@ namespace AppInstaller::Logging std::atomic_uint32_t SubExecutionTelemetryScope::m_sessionId{ s_RootExecutionId }; - SubExecutionTelemetryScope::SubExecutionTelemetryScope() - { - auto expected = s_RootExecutionId; - THROW_HR_IF_MSG(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !s_subExecutionId.compare_exchange_strong(expected, ++m_sessionId), - "Cannot create a sub execution telemetry session when a previous session exists."); - } - - SubExecutionTelemetryScope::~SubExecutionTelemetryScope() - { - s_subExecutionId = s_RootExecutionId; - } + SubExecutionTelemetryScope::SubExecutionTelemetryScope() + { + auto expected = s_RootExecutionId; + THROW_HR_IF_MSG(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !s_subExecutionId.compare_exchange_strong(expected, ++m_sessionId), + "Cannot create a sub execution telemetry session when a previous session exists."); + } + + SubExecutionTelemetryScope::~SubExecutionTelemetryScope() + { + s_subExecutionId = s_RootExecutionId; + } } \ No newline at end of file diff --git a/src/AppInstallerCommonCore/ExperimentalFeature.cpp b/src/AppInstallerCommonCore/ExperimentalFeature.cpp index bc20d3648d..a5b9b437cb 100644 --- a/src/AppInstallerCommonCore/ExperimentalFeature.cpp +++ b/src/AppInstallerCommonCore/ExperimentalFeature.cpp @@ -25,6 +25,8 @@ namespace AppInstaller::Settings return User().Get(); case Feature::ExperimentalUpgrade: return User().Get(); + case Feature::ExperimentalUninstall: + return User().Get(); default: THROW_HR(E_UNEXPECTED); } @@ -44,6 +46,8 @@ namespace AppInstaller::Settings return ExperimentalFeature{ "List Command", "list", "https://aka.ms/winget-settings", Feature::ExperimentalList }; case Feature::ExperimentalUpgrade: return ExperimentalFeature{ "Upgrade Command", "upgrade", "https://aka.ms/winget-settings", Feature::ExperimentalUpgrade }; + case Feature::ExperimentalUninstall: + return ExperimentalFeature{ "Uninstall Command", "uninstall", "https://aka.ms/winget-settings", Feature::ExperimentalUninstall }; default: THROW_HR(E_UNEXPECTED); } diff --git a/src/AppInstallerCommonCore/Public/AppInstallerErrors.h b/src/AppInstallerCommonCore/Public/AppInstallerErrors.h index 5931c2b573..358cd2570f 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerErrors.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerErrors.h @@ -59,6 +59,8 @@ #define APPINSTALLER_CLI_ERROR_UPDATE_ALL_HAS_FAILURE ((HRESULT)0x8A15002C) #define APPINSTALLER_CLI_ERROR_INSTALLER_SECURITY_CHECK_FAILED ((HRESULT)0x8A15002D) #define APPINSTALLER_CLI_ERROR_DOWNLOAD_SIZE_MISMATCH ((HRESULT)0x8A15002E) +#define APPINSTALLER_CLI_ERROR_NO_UNINSTALL_INFO_FOUND ((HRESULT)0x8a15002F) +#define APPINSTALLER_CLI_ERROR_EXEC_UNINSTALL_COMMAND_FAILED ((HRESULT)0x8a150030) namespace AppInstaller { diff --git a/src/AppInstallerCommonCore/Public/AppInstallerTelemetry.h b/src/AppInstallerCommonCore/Public/AppInstallerTelemetry.h index 852cc4c7eb..3bfbbb8a9e 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerTelemetry.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerTelemetry.h @@ -89,6 +89,9 @@ namespace AppInstaller::Logging // Logs a failed installation attempt. void LogInstallerFailure(std::string_view id, std::string_view version, std::string_view channel, std::string_view type, uint32_t errorCode); + // Logs a failed uninstallation attempt. + void LogUninstallerFailure(std::string_view id, std::string_view version, std::string_view type, uint32_t errorCode); + // Logs a failure to insert a value into the in-memory cache of installed system packages. // The most likely reason is due to the same key name being used under multiple ARP scope/architecture locations. void LogDuplicateARPEntry(HRESULT hr, std::string_view scope, std::string_view architecture, std::string_view productCode, std::string_view name); diff --git a/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h b/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h index 658262e3c0..23ebf50206 100644 --- a/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h +++ b/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h @@ -24,7 +24,8 @@ namespace AppInstaller::Settings ExperimentalMSStore = 0x4, ExperimentalList = 0x8, ExperimentalUpgrade = 0x10, - Max = 0x11, // This MUST always be last + ExperimentalUninstall = 0x20, + Max, // This MUST always be last }; using Feature_t = std::underlying_type_t; diff --git a/src/AppInstallerCommonCore/Public/winget/UserSettings.h b/src/AppInstallerCommonCore/Public/winget/UserSettings.h index ee79c6df43..5475012d2a 100644 --- a/src/AppInstallerCommonCore/Public/winget/UserSettings.h +++ b/src/AppInstallerCommonCore/Public/winget/UserSettings.h @@ -51,6 +51,7 @@ namespace AppInstaller::Settings EFExperimentalMSStore, EFList, EFExperimentalUpgrade, + EFUninstall, Max }; @@ -85,6 +86,7 @@ namespace AppInstaller::Settings SETTINGMAPPING_SPECIALIZATION(Setting::EFExperimentalMSStore, bool, bool, false, ".experimentalFeatures.experimentalMSStore"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EFList, bool, bool, false, ".experimentalFeatures.list"sv); SETTINGMAPPING_SPECIALIZATION(Setting::EFExperimentalUpgrade, bool, bool, false, ".experimentalFeatures.upgrade"sv); + SETTINGMAPPING_SPECIALIZATION(Setting::EFUninstall, bool, bool, false, "experimentalFeatures.uninstall"sv); // Used to deduce the SettingVariant type; making a variant that includes std::monostate and all SettingMapping types. template diff --git a/src/AppInstallerCommonCore/UserSettings.cpp b/src/AppInstallerCommonCore/UserSettings.cpp index 7ebe947fa5..cf030387e1 100644 --- a/src/AppInstallerCommonCore/UserSettings.cpp +++ b/src/AppInstallerCommonCore/UserSettings.cpp @@ -210,6 +210,12 @@ namespace AppInstaller::Settings { return value; } + + std::optional::value_t> + SettingMapping::Validate(const SettingMapping::json_t& value) + { + return value; + } } UserSettings::UserSettings() : m_type(UserSettingsType::Default) diff --git a/src/AppInstallerRepositoryCore/Microsoft/ARPHelper.cpp b/src/AppInstallerRepositoryCore/Microsoft/ARPHelper.cpp index 8d1b470579..7eaf44b40c 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/ARPHelper.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/ARPHelper.cpp @@ -152,9 +152,19 @@ namespace AppInstaller::Repository::Microsoft void ARPHelper::AddMetadataIfPresent(const Registry::Key& key, const std::wstring& name, SQLiteIndex& index, SQLiteIndex::IdType manifestId, PackageVersionMetadata metadata) { auto value = key[name]; - if (value && value->GetType() == Registry::Value::Type::String) + if (value) { - auto valueString = value->GetValue(); + std::string valueString; + + if (value->GetType() == Registry::Value::Type::String) + { + valueString = value->GetValue(); + } + else if (value->GetType() == Registry::Value::Type::ExpandString) + { + valueString = value->GetValue(); + } + if (!valueString.empty()) { index.SetMetadataByManifestId(manifestId, metadata, valueString); diff --git a/src/AppInstallerTestExeInstaller/main.cpp b/src/AppInstallerTestExeInstaller/main.cpp index 306265b2bb..8ba5116466 100644 --- a/src/AppInstallerTestExeInstaller/main.cpp +++ b/src/AppInstallerTestExeInstaller/main.cpp @@ -14,7 +14,8 @@ using namespace std::filesystem; std::string_view registrySubkey = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\"; std::string_view defaultProductID = "{A499DD5E-8DC5-4AD2-911A-BCD0263295E9}"; -std::wstring GenerateUninstaller(std::ostream& out, const path& installDirectory) { +path GenerateUninstaller(std::ostream& out, const path& installDirectory, const std::string& productID) +{ path uninstallerPath = installDirectory; uninstallerPath /= "UninstallTestExe.bat"; @@ -23,17 +24,28 @@ std::wstring GenerateUninstaller(std::ostream& out, const path& installDirectory path uninstallerOutputTextFilePath = installDirectory; uninstallerOutputTextFilePath /= "TestExeUninstalled.txt"; - // TODO: Needs to re-invoke the installer and remove the Uninstall key that it added + std::string registryKey{ "HKEY_CURRENT_USER\\" }; + registryKey += registrySubkey; + if (!productID.empty()) + { + registryKey += productID; + } + else + { + registryKey += defaultProductID; + } + std::ofstream uninstallerScript(uninstallerPath); uninstallerScript << "@echo off\n"; uninstallerScript << "ECHO. >" << uninstallerOutputTextFilePath << "\n"; uninstallerScript << "ECHO AppInstallerTestExeInstaller.exe uninstalled successfully.\n"; + uninstallerScript << "REG DELETE " << registryKey << " /f\n"; uninstallerScript.close(); - return uninstallerPath.wstring(); + return uninstallerPath; } -void WriteToUninstallRegistry(std::ostream& out, const std::wstring& productID, const std::wstring& uninstallerPath) +void WriteToUninstallRegistry(std::ostream& out, const std::string& productID, const path& uninstallerPath) { HKEY hkey; LONG lReg; @@ -45,20 +57,20 @@ void WriteToUninstallRegistry(std::ostream& out, const std::wstring& productID, const wchar_t* uninstallString = uninstallerPath.c_str(); DWORD version = 1; - path registryKey{ registrySubkey }; + std::string registryKey{ registrySubkey }; if (!productID.empty()) { - registryKey /= productID; + registryKey += productID; out << "Product Code overridden to: " << registryKey << "\n"; } else { - registryKey /= defaultProductID; + registryKey += defaultProductID; out << "Default Product Code used: " << registryKey << "\n"; } - lReg = RegCreateKeyEx( + lReg = RegCreateKeyExA( HKEY_CURRENT_USER, registryKey.c_str(), 0, @@ -69,8 +81,8 @@ void WriteToUninstallRegistry(std::ostream& out, const std::wstring& productID, &hkey, NULL); - if (lReg == ERROR_SUCCESS) { - + if (lReg == ERROR_SUCCESS) + { out << "Successfully opened registry key \n"; // Set Display Name Property Value @@ -116,9 +128,8 @@ void WriteToUninstallRegistry(std::ostream& out, const std::wstring& productID, int main(int argc, const char** argv) { path installDirectory = temp_directory_path(); - std::wstringstream productCodeStream; std::stringstream outContent; - std::wstring productCode; + std::string productCode; // Output to cout by default, but swap to a file if requested std::ostream* out = &std::cout; @@ -138,7 +149,7 @@ int main(int argc, const char** argv) // Supports custom product code ID if (_stricmp(argv[i], "/ProductID") == 0 && ++i < argc) { - productCodeStream << argv[i]; + productCode = argv[i]; } // Supports log file @@ -157,16 +168,9 @@ int main(int argc, const char** argv) file.close(); - if (!productCodeStream.str().empty()) - { - productCode = productCodeStream.str(); - } - - std::wstring uninstallerPath = GenerateUninstaller(*out, installDirectory); + path uninstallerPath = GenerateUninstaller(*out, installDirectory, productCode); WriteToUninstallRegistry(*out, productCode, uninstallerPath); return 0; } - - From 3801331f034585d048853c73682def6a4562fe0f Mon Sep 17 00:00:00 2001 From: David Allsopp Date: Thu, 17 Dec 2020 23:04:28 +0000 Subject: [PATCH 05/47] Ensure progress widgets respect UseVT (#667) --- src/AppInstallerCLICore/ExecutionProgress.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/AppInstallerCLICore/ExecutionProgress.cpp b/src/AppInstallerCLICore/ExecutionProgress.cpp index 609f04db54..fef6d891f3 100644 --- a/src/AppInstallerCLICore/ExecutionProgress.cpp +++ b/src/AppInstallerCLICore/ExecutionProgress.cpp @@ -133,11 +133,13 @@ namespace AppInstaller::CLI::Execution { void ProgressVisualizerBase::ApplyStyle(size_t i, size_t max, bool enabled) { + if (!UseVT()) + { + // Either no style set or VT disabled + return; + } switch (m_style) { - case VisualStyle::NoVT: - // No VT means no style set - break; case VisualStyle::Retro: if (enabled) { From 6f7fb63d94ef2d56ff310efaca98b1c1949ae41e Mon Sep 17 00:00:00 2001 From: denelon Date: Fri, 8 Jan 2021 11:24:00 -0800 Subject: [PATCH 06/47] January 2021 Roadmap update (#690) January 20201 update --- doc/windows-package-manager-v1-roadmap.md | 85 ++++++++++++----------- 1 file changed, 45 insertions(+), 40 deletions(-) diff --git a/doc/windows-package-manager-v1-roadmap.md b/doc/windows-package-manager-v1-roadmap.md index 10f3c97d61..49ff98211b 100644 --- a/doc/windows-package-manager-v1-roadmap.md +++ b/doc/windows-package-manager-v1-roadmap.md @@ -45,15 +45,17 @@ Ultimately, we're aiming for Windows Package Manager v1.0 to be released in Spri | July 2020 | | | | August 2020 | | | | September 2020 | [v0.2](https://github.com/microsoft/winget-cli/milestone/4) | Support for Microsoft Store (curated list of developer tools in experimental feature)| -| October 2020 | [v0.3](https://github.com/microsoft/winget-cli/milestone/5), [v0.4](https://github.com/microsoft/winget-cli/milestone/6), [v0.5](https://github.com/microsoft/winget-cli/milestone/7) | List, Upgrade, and Uninstall (includes Apps in Control Panel/Add Remove Programs) | -| November 2020 | [v0.6](https://github.com/microsoft/winget-cli/milestone/8), [v0.7](https://github.com/microsoft/winget-cli/milestone/9) | Import / Export and Dependency Support| -| December 2020 | [v0.8](https://github.com/microsoft/winget-cli/milestone/10) | Multiple Architectures | -| January 2021 | [v0.9](https://github.com/microsoft/winget-cli/milestone/11) | Multiple Languages | -| February 2021 | [v0.10](https://github.com/microsoft/winget-cli/milestone/12) | User vs. System installation | -| March 2021 | [v0.11](https://github.com/microsoft/winget-cli/milestone/13) | Third party REST source| -| April 2021 | [v0.12](https://github.com/microsoft/winget-cli/milestone/14) | Group Policy| +| October 2020 | [v0.3](https://github.com/microsoft/winget-cli/milestone/5) | List (includes Apps in Control Panel/Add Remove Programs) | +| November 2020 | [v0.4](https://github.com/microsoft/winget-cli/milestone/6) | Upgrade | +| December 2020 | [v0.5](https://github.com/microsoft/winget-cli/milestone/7) | Uninstall | +| January 2021 | [v0.6](https://github.com/microsoft/winget-cli/milestone/8) | Import / Export | +| February 2021 | , [v0.7](https://github.com/microsoft/winget-cli/milestone/9), [v0.8](https://github.com/microsoft/winget-cli/milestone/10), [v0.9](https://github.com/microsoft/winget-cli/milestone/11), [v0.10](https://github.com/microsoft/winget-cli/milestone/12) | Dependency Support, Multiple Architectures, Multiple Languages, and User vs. System installation | +| March 2021 | [v0.11](https://github.com/microsoft/winget-cli/milestone/13), [v0.12](https://github.com/microsoft/winget-cli/milestone/14), [v0.13](https://github.com/microsoft/winget-cli/milestone/15), [v0.14](https://github.com/microsoft/winget-cli/milestone/16), [v0.16](https://github.com/microsoft/winget-cli/milestone/18), [v0.17](https://github.com/microsoft/winget-cli/milestone/19) | Third party REST source, Group Policy, Delivery Optimization, Metered Networks, .zip, and .exe| +| April 2021 || Group Policy| | May 2021 | [v1.0](https://github.com/microsoft/winget-cli/milestone/1) | Windows Package Manager v1.0 Release | +Note: Many of the features have been implemented in experimental mode. If you execute `winget features` a list of experimental features and their status is displayed. You may modify your settings file with `winget settings` to enable or disable them. The experimental "list" feature is a prerequisite for "upgrade", "uninstall", and other features in development. Once the "list" feature has been fully implemented, the other stable features depending on it will also be migrated from experimental to default. + ## GitHub Milestones Each Release above is/will be reflected in our [GitHub milestones](https://github.com/microsoft/winget-cli/milestones): @@ -74,7 +76,7 @@ Each Release above is/will be reflected in our [GitHub milestones](https://githu | [v0.12](https://github.com/microsoft/winget-cli/milestone/14) | Group Policy | | [v0.13](https://github.com/microsoft/winget-cli/milestone/15) | Delivery Optimization | | [v0.14](https://github.com/microsoft/winget-cli/milestone/16) | Metered Networks | -| [v0.15](https://github.com/microsoft/winget-cli/milestone/17) | App Config Files | +| ~~[v0.15](https://github.com/microsoft/winget-cli/milestone/17)~~ | App Config Files* | | [v0.16](https://github.com/microsoft/winget-cli/milestone/18) | .zip | | [v0.17](https://github.com/microsoft/winget-cli/milestone/19) | .exe | | [v0.18](https://github.com/microsoft/winget-cli/milestone/20) | Portable/Standalone Apps | @@ -93,6 +95,8 @@ Each Release above is/will be reflected in our [GitHub milestones](https://githu | [v0.31](https://github.com/microsoft/winget-cli/milestone/33) | Auto Upgrade Apps | | [Backlog](https://github.com/microsoft/winget-cli/milestone/2) | Work not yet assigned to a milestone or release | +* Versions with strikethroug have been pushed post v1.0. + ## Issue Triage & Prioritization Incoming issues/asks/etc. are triaged several times a week, labelled appropriately, and assigned to a milestone in priority order: @@ -113,39 +117,40 @@ The following are a list of the key scenarios we're aiming to deliver for Window | [v0.1.41821-preview](https://github.com/microsoft/winget-cli/releases/tag/v0.1.41821-preview) | Configurability & Customization | The client will have a modern, flexible settings mechanism that persists settings to/from a JSON file stored in the user's app data folders, and/or in files synchronized between machines via OneDrive, etc. | | [v0.1.41821-preview](https://github.com/microsoft/winget-cli/releases/tag/v0.1.41821-preview) | Color Theming & Styling | The client will honor the user's Windows dark/light theme settings, and/or color accent settings. | | [v0.1.42241-preview](https://github.com/microsoft/winget-cli/releases/tag/v0.1.42241-preview) | Autocomplete | The client will support autocomplete for all commands and packages in the local cache. | -| V1 | #119 `winget list` | The client should be able to tell you what Apps are installed including the Control Panel. | -| V1 | #120 `winget upgrade` | The client should be able to update one or "all" installed Apps. | -| V1 | #121 `winget uninstall` | The client should be able to uninstall Apps. | -| V1 | #220 Export/Import | The client should be able to export the list of installed Apps and import the exported list. | -| V1 | #163 Dependencies | The client should be able to install package dependencies. | -| V1 | #132 Multiple Architectures | The client should support multiple architectures in the same manifest. | -| V1 | #149 User vs. System | Applications may be installed for the local user or for the system. | -| V1 | #124 Multiple Languages | The client will support installation for almost every language for which there is a fixed-width font including East Asian languages. Bonus points for RTL languages/scripts. | -| V1 | #226 REST Repository | Support for a REST based repository. | -| V1 | #154 Group Policy | Support for Group Policy control. | -| V1 | #151 Delivery Optimization | Delivery Optimization should be leveraged for large Apps. | -| V1 | #150 Metered Networks | The client should download responsibly when on metered networks. | -| V1 | #158 App Config Files | Support for silent installers that require a configuration file. | -| V1 | #140 Install .zip | The client should be able to install programs in a .zip file. | -| V1 | #194 Install .exe | The client should be able to install a static .exe file. | -| V1 | #182 Install portable app | The client should be able to install portable Apps. | -| V1 | #201 Specify install directory | The client should be able to install to an alternate directory. | -| V1 | #137 Non-Zero Exit Codes | The client should support applications with non-zero exit codes as success. | -| V1 | #279 Opt-Out of Telemetry | The client should be able to Opt-Out of Telemetry. | -| V1 | #161 Client Verbosity Settings | The client should support different verbosity settings. | -| V1 | #147 Release Channels | Some applications have different release channels and we should support them. | -| V1 | #221 Native PowerShell | Native PowerShell support for the client. | -| V1 | #164 Install PWA | Support installing Progressive Web Applications. | -| V1 | #219 Install Multiple Apps | The client should allow a user to specify multiple apps to install. | -| V1 | #229 Suppress reboot | The client should allow a user to suppress reboot as a default setting. | -| V1 | #227 Version specification | The client should allow more variation to specifying package versions for installation. | -| V1 | #225 Parallel download | The client should support multiple connections per package for download. | -| V1 | #166 Fonts | The client should support installing fonts. | -| V1 | #212 Auto Upgrade | The client should be able to auto upgrade installed apps if configured to do so. | -| V1 | #157 Manifest Wizard | Help a user generate a manifest. | -| V1 | #161 Verbosity | Client Verbosity Settings. | -| V1 | #117 Microsoft Store | Support for installing Apps from the Microsoft Store. | +| V1 | [#119](https://github.com/microsoft/winget-cli/issues/119) `winget list` | The client should be able to tell you what Apps are installed including the Control Panel. | +| V1 | [#120](https://github.com/microsoft/winget-cli/issues/120) `winget upgrade` | The client should be able to update one or "all" installed Apps. | +| V1 | [#121](https://github.com/microsoft/winget-cli/issues/121) `winget uninstall` | The client should be able to uninstall Apps. | +| V1 | [#220](https://github.com/microsoft/winget-cli/issues/220) Export/Import | The client should be able to export the list of installed Apps and import the exported list. | +| V1 | [#163](https://github.com/microsoft/winget-cli/issues/163) Dependencies | The client should be able to install package dependencies. | +| V1 | [#132](https://github.com/microsoft/winget-cli/issues/132) Multiple Architectures | The client should support multiple architectures in the same manifest. | +| V1 | [#149](https://github.com/microsoft/winget-cli/issues/149) User vs. System | Applications may be installed for the local user or for the system. | +| V1 | [#124](https://github.com/microsoft/winget-cli/issues/124) Multiple Languages | The client will support installation for almost every language for which there is a fixed-width font including East Asian languages. Bonus points for RTL languages/scripts. | +| V1 | [#226](https://github.com/microsoft/winget-cli/issues/226) REST Repository | Support for a REST based repository. | +| V1 | [#154](https://github.com/microsoft/winget-cli/issues/154) Group Policy | Support for Group Policy control. | +| V1 | [#151](https://github.com/microsoft/winget-cli/issues/151) Delivery Optimization | Delivery Optimization should be leveraged for large Apps. | +| V1 | [#150](https://github.com/microsoft/winget-cli/issues/150) Metered Networks | The client should download responsibly when on metered networks. | +| V1 | [#140](https://github.com/microsoft/winget-cli/issues/140) Install .zip | The client should be able to install programs in a .zip file. | +| V1 | [#194](https://github.com/microsoft/winget-cli/issues/194) Install .exe | The client should be able to install a static .exe file. | +| V1 | [#182](https://github.com/microsoft/winget-cli/issues/182) Install portable app | The client should be able to install portable Apps. | +| V1 | [#201](https://github.com/microsoft/winget-cli/issues/201) Specify install directory | The client should be able to install to an alternate directory. | +| V1 | [#137](https://github.com/microsoft/winget-cli/issues/137) Non-Zero Exit Codes | The client should support applications with non-zero exit codes as success. | +| V1 | [#279](https://github.com/microsoft/winget-cli/issues/279) Opt-Out of Telemetry | The client should be able to Opt-Out of Telemetry. | +| V1 | [#161](https://github.com/microsoft/winget-cli/issues/161) Client Verbosity Settings | The client should support different verbosity settings. | +| V1 | [#147](https://github.com/microsoft/winget-cli/issues/147) Release Channels | Some applications have different release channels and we should support them. | +| V1 | [#221](https://github.com/microsoft/winget-cli/issues/221) Native PowerShell | Native PowerShell support for the client. | +| V1 | [#164](https://github.com/microsoft/winget-cli/issues/164) Install PWA | Support installing Progressive Web Applications. | +| V1 | [#219](https://github.com/microsoft/winget-cli/issues/219) Install Multiple Apps | The client should allow a user to specify multiple apps to install. | +| V1 | [#229](https://github.com/microsoft/winget-cli/issues/229) Suppress reboot | The client should allow a user to suppress reboot as a default setting. | +| V1 | [#227](https://github.com/microsoft/winget-cli/issues/227) Version specification | The client should allow more variation to specifying package versions for installation. | +| V1 | [#225](https://github.com/microsoft/winget-cli/issues/225) Parallel download | The client should support multiple connections per package for download. | +| V1 | [#166](https://github.com/microsoft/winget-cli/issues/166) Fonts | The client should support installing fonts. | +| V1 | [#212](https://github.com/microsoft/winget-cli/issues/212) Auto Upgrade | The client should be able to auto upgrade installed apps if configured to do so. | +| V1 | [#157](https://github.com/microsoft/winget-cli/issues/157) Manifest Wizard | Help a user generate a manifest. | +| V1 | [#161](https://github.com/microsoft/winget-cli/issues/161) Verbosity | Client Verbosity Settings. | +| V1 | [#117](https://github.com/microsoft/winget-cli/issues/117) Microsoft Store | Support for installing Apps from the Microsoft Store. | | V1 | Accessibility (A11y) | The client will be highly accessible and inclusive. It will expose its contents via [UIA](https://docs.microsoft.com/en-us/dotnet/framework/ui-automation/ui-automation-overview) to support tools such as [Windows Narrator](https://support.microsoft.com/en-us/help/22798/windows-10-complete-guide-to-narrator), and UI automation tools including [WinAppDriver](https://github.com/Microsoft/WinAppDriver). | +| V1.x | [#158](https://github.com/microsoft/winget-cli/issues/158) App Config Files | Support for silent installers that require a configuration file. | + Feature Notes: \* Feature Priorities will be influenced by community feedback on issues. From 380d8f21405352d0a2a47d60f41e021918593518 Mon Sep 17 00:00:00 2001 From: denelon Date: Fri, 8 Jan 2021 12:37:13 -0800 Subject: [PATCH 07/47] Update windows-package-manager-v1-roadmap.md (#691) Fixed April 2021 --- doc/windows-package-manager-v1-roadmap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/windows-package-manager-v1-roadmap.md b/doc/windows-package-manager-v1-roadmap.md index 49ff98211b..fc0fcfae74 100644 --- a/doc/windows-package-manager-v1-roadmap.md +++ b/doc/windows-package-manager-v1-roadmap.md @@ -51,7 +51,7 @@ Ultimately, we're aiming for Windows Package Manager v1.0 to be released in Spri | January 2021 | [v0.6](https://github.com/microsoft/winget-cli/milestone/8) | Import / Export | | February 2021 | , [v0.7](https://github.com/microsoft/winget-cli/milestone/9), [v0.8](https://github.com/microsoft/winget-cli/milestone/10), [v0.9](https://github.com/microsoft/winget-cli/milestone/11), [v0.10](https://github.com/microsoft/winget-cli/milestone/12) | Dependency Support, Multiple Architectures, Multiple Languages, and User vs. System installation | | March 2021 | [v0.11](https://github.com/microsoft/winget-cli/milestone/13), [v0.12](https://github.com/microsoft/winget-cli/milestone/14), [v0.13](https://github.com/microsoft/winget-cli/milestone/15), [v0.14](https://github.com/microsoft/winget-cli/milestone/16), [v0.16](https://github.com/microsoft/winget-cli/milestone/18), [v0.17](https://github.com/microsoft/winget-cli/milestone/19) | Third party REST source, Group Policy, Delivery Optimization, Metered Networks, .zip, and .exe| -| April 2021 || Group Policy| +| April 2021 ||| | May 2021 | [v1.0](https://github.com/microsoft/winget-cli/milestone/1) | Windows Package Manager v1.0 Release | Note: Many of the features have been implemented in experimental mode. If you execute `winget features` a list of experimental features and their status is displayed. You may modify your settings file with `winget settings` to enable or disable them. The experimental "list" feature is a prerequisite for "upgrade", "uninstall", and other features in development. Once the "list" feature has been fully implemented, the other stable features depending on it will also be migrated from experimental to default. From b1538828de7ef3af1f3d4ba9659ed3f414df8d85 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Mon, 11 Jan 2021 14:19:51 -0500 Subject: [PATCH 08/47] spelling: strikethrough (#692) --- .github/actions/spelling/allow.txt | 1 + doc/windows-package-manager-v1-roadmap.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt index ac77b33efe..e1eff4a27e 100644 --- a/.github/actions/spelling/allow.txt +++ b/.github/actions/spelling/allow.txt @@ -353,6 +353,7 @@ strcoll streamoff streampos stricmp +strikethrough STRINGID STRINGIFY STRINGIZE diff --git a/doc/windows-package-manager-v1-roadmap.md b/doc/windows-package-manager-v1-roadmap.md index fc0fcfae74..c3d16dbe06 100644 --- a/doc/windows-package-manager-v1-roadmap.md +++ b/doc/windows-package-manager-v1-roadmap.md @@ -95,7 +95,7 @@ Each Release above is/will be reflected in our [GitHub milestones](https://githu | [v0.31](https://github.com/microsoft/winget-cli/milestone/33) | Auto Upgrade Apps | | [Backlog](https://github.com/microsoft/winget-cli/milestone/2) | Work not yet assigned to a milestone or release | -* Versions with strikethroug have been pushed post v1.0. +* Versions with strikethrough have been pushed post v1.0. ## Issue Triage & Prioritization From 69290f9695649fad58aab5c48b2132a0a1cd1072 Mon Sep 17 00:00:00 2001 From: JohnMcPMS Date: Mon, 11 Jan 2021 16:03:19 -0800 Subject: [PATCH 09/47] Force output paths to use u8string with filesystem::path (#694) ## Issue When paths with non-ASCII characters are used, logging them is resulting in an exception due to not being able to convert them to ASCII (the default behavior when forced to convert to a narrow char). ## Change To better handle all situations of output of a path, both the logging and reporting infrastructure have been enlightened to force the use of `u8String()` when they see one. --- src/AppInstallerCLICore/ChannelStreams.cpp | 7 ++++ src/AppInstallerCLICore/ChannelStreams.h | 1 + src/AppInstallerCLIE2ETests/SourceCommand.cs | 16 ++++----- src/AppInstallerCLITests/Strings.cpp | 17 ++++++++++ .../Public/AppInstallerLogging.h | 33 ++++++++++++++++++- .../Public/winget/LocIndependent.h | 10 +++--- 6 files changed, 70 insertions(+), 14 deletions(-) diff --git a/src/AppInstallerCLICore/ChannelStreams.cpp b/src/AppInstallerCLICore/ChannelStreams.cpp index d737e39abb..9dc2013a80 100644 --- a/src/AppInstallerCLICore/ChannelStreams.cpp +++ b/src/AppInstallerCLICore/ChannelStreams.cpp @@ -85,6 +85,13 @@ namespace AppInstaller::CLI::Execution return *this; } + OutputStream& OutputStream::operator<<(const std::filesystem::path& path) + { + ApplyFormat(); + m_out << path.u8string(); + return *this; + } + NoVTStream::NoVTStream(std::ostream& out, bool enabled) : m_out(out, enabled, false) {} diff --git a/src/AppInstallerCLICore/ChannelStreams.h b/src/AppInstallerCLICore/ChannelStreams.h index 22a234442a..d3b7df2315 100644 --- a/src/AppInstallerCLICore/ChannelStreams.h +++ b/src/AppInstallerCLICore/ChannelStreams.h @@ -100,6 +100,7 @@ namespace AppInstaller::CLI::Execution OutputStream& operator<<(std::ostream& (__cdecl* f)(std::ostream&)); OutputStream& operator<<(const VirtualTerminal::Sequence& sequence); OutputStream& operator<<(const VirtualTerminal::ConstructedSequence& sequence); + OutputStream& operator<<(const std::filesystem::path& path); private: // Applies the format for the stream. diff --git a/src/AppInstallerCLIE2ETests/SourceCommand.cs b/src/AppInstallerCLIE2ETests/SourceCommand.cs index 4837eb015f..f69dc3622d 100644 --- a/src/AppInstallerCLIE2ETests/SourceCommand.cs +++ b/src/AppInstallerCLIE2ETests/SourceCommand.cs @@ -29,7 +29,7 @@ public void SourceAddWithDuplicateName() public void SourceAddWithInvalidURL() { // Add source with invalid url should fail - var result = TestCommon.RunAICLICommand("source add", "AnotherSource https://microsoft.com"); + var result = TestCommon.RunAICLICommand("source add", $"AnotherSource {Constants.TestSourceUrl}/Invalid/Directory/Dont/Add/Me"); Assert.AreEqual(Constants.ErrorCode.HTTP_E_STATUS_NOT_FOUND, result.ExitCode); Assert.True(result.StdOut.Contains("An unexpected error occurred while executing the command")); } @@ -50,7 +50,7 @@ public void SourceListWithNoArgs() // List with no args should list all available sources var result = TestCommon.RunAICLICommand("source list", ""); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); - Assert.True(result.StdOut.Contains("https://localhost:5001/TestKit")); + Assert.True(result.StdOut.Contains(Constants.TestSourceUrl)); } [Test] @@ -58,8 +58,8 @@ public void SourceListWithName() { var result = TestCommon.RunAICLICommand("source list", $"-n {Constants.TestSourceName}"); Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode); - Assert.True(result.StdOut.Contains("TestSource")); - Assert.True(result.StdOut.Contains("https://localhost:5001/TestKit")); + Assert.True(result.StdOut.Contains(Constants.TestSourceName)); + Assert.True(result.StdOut.Contains(Constants.TestSourceUrl)); Assert.True(result.StdOut.Contains("Microsoft.PreIndexed.Package")); Assert.True(result.StdOut.Contains("Updated")); } @@ -110,8 +110,8 @@ public void SourceReset() { var result = TestCommon.RunAICLICommand("source reset", ""); Assert.True(result.StdOut.Contains("The following sources will be reset if the --force option is given:")); - Assert.True(result.StdOut.Contains("TestSource")); - Assert.True(result.StdOut.Contains("https://localhost:5001/TestKit")); + Assert.True(result.StdOut.Contains(Constants.TestSourceName)); + Assert.True(result.StdOut.Contains(Constants.TestSourceUrl)); } [Test] @@ -126,8 +126,8 @@ public void SourceForceReset() result = TestCommon.RunAICLICommand("source list", ""); Assert.True(result.StdOut.Contains("winget")); Assert.True(result.StdOut.Contains("https://winget.azureedge.net/cache")); - Assert.False(result.StdOut.Contains($"{Constants.TestSourceName}")); - Assert.False(result.StdOut.Contains($"{Constants.TestSourceUrl}")); + Assert.False(result.StdOut.Contains(Constants.TestSourceName)); + Assert.False(result.StdOut.Contains(Constants.TestSourceUrl)); ResetTestSource(); } } diff --git a/src/AppInstallerCLITests/Strings.cpp b/src/AppInstallerCLITests/Strings.cpp index a4cbea78f8..02ea9c49fb 100644 --- a/src/AppInstallerCLITests/Strings.cpp +++ b/src/AppInstallerCLITests/Strings.cpp @@ -3,6 +3,7 @@ #include "pch.h" #include "TestCommon.h" #include +#include using namespace std::string_view_literals; using namespace AppInstaller::Utility; @@ -147,3 +148,19 @@ TEST_CASE("ExpandEnvironmentVariables", "[strings]") REQUIRE(ExpandEnvironmentVariables(L"%TEMP%") == tempPath); } + +TEST_CASE("PathOutput", "[strings]") +{ + std::string original = "\xe6\xb5\x8b\xe8\xaf\x95"; + std::filesystem::path path = ConvertToUTF16(original); + AICLI_LOG(Test, Info, << path); + + std::istringstream in; + std::ostringstream out; + AppInstaller::CLI::Execution::Reporter reporter{ out, in }; + + reporter.Info() << path; + + std::string output = out.str(); + REQUIRE(output.substr(output.size() - original.size()) == original); +} diff --git a/src/AppInstallerCommonCore/Public/AppInstallerLogging.h b/src/AppInstallerCommonCore/Public/AppInstallerLogging.h index cea60aa3fa..e6b7163e09 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerLogging.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerLogging.h @@ -17,7 +17,7 @@ auto& _aicli_log_log = AppInstaller::Logging::Log(); \ if (_aicli_log_log.IsEnabled(_aicli_log_channel, _aicli_log_level)) \ { \ - std::stringstream _aicli_log_strstr; \ + AppInstaller::Logging::LoggingStream _aicli_log_strstr; \ _aicli_log_strstr _outstream_; \ _aicli_log_log.Write(_aicli_log_channel, _aicli_log_level, _aicli_log_strstr.str()); \ } \ @@ -140,6 +140,37 @@ namespace AppInstaller::Logging // Calls the various stream format functions to produce an 8 character hexadecimal output. std::ostream& SetHRFormat(std::ostream& out); + + // This type allows us to override the default behavior of output operators for logging. + struct LoggingStream + { + // Force use of the UTF-8 string from a file path. + // This should not be necessary when we move to C++20 and convert to using u8string. + friend AppInstaller::Logging::LoggingStream& operator<<(AppInstaller::Logging::LoggingStream& out, std::filesystem::path& path) + { + out.m_out << path.u8string(); + return out; + } + + friend AppInstaller::Logging::LoggingStream& operator<<(AppInstaller::Logging::LoggingStream& out, const std::filesystem::path& path) + { + out.m_out << path.u8string(); + return out; + } + + // Everything else. + template + friend AppInstaller::Logging::LoggingStream& operator<<(AppInstaller::Logging::LoggingStream& out, T&& t) + { + out.m_out << std::forward(t); + return out; + } + + std::string str() const { return m_out.str(); } + + private: + std::stringstream m_out; + }; } // Enable output of system_clock time_points. diff --git a/src/AppInstallerCommonCore/Public/winget/LocIndependent.h b/src/AppInstallerCommonCore/Public/winget/LocIndependent.h index 5774ab8d10..6a629982de 100644 --- a/src/AppInstallerCommonCore/Public/winget/LocIndependent.h +++ b/src/AppInstallerCommonCore/Public/winget/LocIndependent.h @@ -53,12 +53,12 @@ namespace AppInstaller::Utility bool operator<(const LocIndString& other) const { return m_value < other.m_value; } + friend std::ostream& operator<<(std::ostream& out, const AppInstaller::Utility::LocIndString& lis) + { + return (out << lis.get()); + } + private: std::string m_value; }; } - -inline std::ostream& operator<<(std::ostream& out, const AppInstaller::Utility::LocIndString& lis) -{ - return (out << lis.get()); -} From eeca649172c13e79d15eea025acdc6d178fb8469 Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Mon, 11 Jan 2021 18:23:52 -0800 Subject: [PATCH 10/47] Squashed 'src/Valijson/valijson/' content from commit 7a52fc8 git-subtree-dir: src/Valijson/valijson git-subtree-split: 7a52fc88cdffd6678c009ca2fad700151f7363c6 --- .gitignore | 6 + .gitmodules | 0 .travis.yml | 74 + Authors | 35 + CMakeLists.txt | 163 + Doxyfile | 2362 ++ LICENSE | 23 + README.md | 200 + cmake/CMakeFindExtensions.cmake | 375 + cmake/CMakeHelpers.cmake | 256 + cmake/FindPoco.cmake | 111 + cmake/Findcurlpp.cmake | 20 + cmake/README | 4 + doc/schema/draft-03.json | 174 + doc/schema/draft-04.json | 150 + .../draft-fge-json-schema-validation-00.txt | 1400 ++ .../draft-luff-json-hyper-schema-00.txt | 1512 ++ .../draft-pbryan-zyp-json-ref-03.txt | 280 + .../draft-zyp-json-schema-03.txt | 1569 ++ .../draft-zyp-json-schema-04.txt | 784 + doc/specifications/rfc3986-uri.txt | 3419 +++ doc/specifications/rfc4627-json.txt | 563 + doc/specifications/rfc6901-json-pointer.txt | 451 + examples/array_iteration_basics.cpp | 132 + examples/array_iteration_template_fn.cpp | 65 + examples/custom_schema.cpp | 224 + examples/external_schema.cpp | 87 + examples/json_pointers.cpp | 110 + examples/object_iteration.cpp | 69 + examples/remote_resolution.cpp | 107 + include/compat/optional.hpp | 1042 + include/valijson/adapters/adapter.hpp | 472 + include/valijson/adapters/basic_adapter.hpp | 864 + include/valijson/adapters/frozen_value.hpp | 52 + include/valijson/adapters/json11_adapter.hpp | 709 + include/valijson/adapters/jsoncpp_adapter.hpp | 720 + .../adapters/nlohmann_json_adapter.hpp | 707 + .../valijson/adapters/picojson_adapter.hpp | 723 + .../valijson/adapters/poco_json_adapter.hpp | 718 + .../adapters/property_tree_adapter.hpp | 752 + include/valijson/adapters/qtjson_adapter.hpp | 720 + .../valijson/adapters/rapidjson_adapter.hpp | 929 + .../valijson/adapters/std_string_adapter.hpp | 484 + include/valijson/constraint_builder.hpp | 21 + .../valijson/constraints/basic_constraint.hpp | 65 + .../constraints/concrete_constraints.hpp | 1231 + include/valijson/constraints/constraint.hpp | 50 + .../constraints/constraint_visitor.hpp | 104 + .../valijson/internal/custom_allocator.hpp | 107 + include/valijson/internal/debug.hpp | 30 + include/valijson/internal/json_pointer.hpp | 255 + include/valijson/internal/json_reference.hpp | 60 + include/valijson/internal/optional.hpp | 14 + include/valijson/internal/uri.hpp | 33 + include/valijson/schema.hpp | 212 + include/valijson/schema_parser.hpp | 2394 ++ include/valijson/subschema.hpp | 295 + include/valijson/utils/file_utils.hpp | 44 + include/valijson/utils/json11_utils.hpp | 34 + include/valijson/utils/jsoncpp_utils.hpp | 33 + .../valijson/utils/nlohmann_json_utils.hpp | 34 + include/valijson/utils/picojson_utils.hpp | 33 + include/valijson/utils/poco_json_utils.hpp | 37 + .../valijson/utils/property_tree_utils.hpp | 36 + include/valijson/utils/qtjson_utils.hpp | 44 + include/valijson/utils/rapidjson_utils.hpp | 35 + include/valijson/utils/utf8_utils.hpp | 60 + include/valijson/validation_results.hpp | 122 + include/valijson/validation_visitor.hpp | 1822 ++ include/valijson/validator.hpp | 74 + .../documents/array_doubles_10_20_30_40.json | 1 + tests/data/documents/array_doubles_1_2_3.json | 1 + .../data/documents/array_doubles_1_2_3_4.json | 1 + tests/data/documents/array_empty.json | 1 + .../documents/array_integers_10_20_30_40.json | 1 + .../data/documents/array_integers_1_2_3.json | 1 + .../documents/array_integers_1_2_3_4.json | 1 + .../documents/array_strings_10_20_30_40.json | 1 + tests/data/documents/array_strings_1_2_3.json | 1 + .../data/documents/array_strings_1_2_3_4.json | 1 + tests/data/documents/object_empty.json | 1 + .../allof_integers_and_numbers.schema.json | 20 + tests/test_adapter_comparison.cpp | 537 + tests/test_fetch_document_callback.cpp | 80 + tests/test_json11_adapter.cpp | 82 + tests/test_json_pointer.cpp | 317 + tests/test_jsoncpp_adapter.cpp | 80 + tests/test_nlohmann_json_adapter.cpp | 79 + tests/test_picojson_adapter.cpp | 84 + tests/test_poco_json_adapter.cpp | 84 + tests/test_poly_constraint.cpp | 97 + tests/test_property_tree_adapter.cpp | 87 + tests/test_qtjson_adapter.cpp | 84 + tests/test_rapidjson_adapter.cpp | 109 + tests/test_validation_errors.cpp | 96 + tests/test_validator.cpp | 578 + thirdparty/JSON-Schema-Test-Suite/.gitignore | 1 + thirdparty/JSON-Schema-Test-Suite/.travis.yml | 9 + thirdparty/JSON-Schema-Test-Suite/LICENSE | 19 + thirdparty/JSON-Schema-Test-Suite/README.md | 181 + thirdparty/JSON-Schema-Test-Suite/index.js | 45 + .../JSON-Schema-Test-Suite/package.json | 28 + .../remotes/folder/folderInteger.json | 3 + .../remotes/integer.json | 3 + .../remotes/name-defs.json | 11 + .../JSON-Schema-Test-Suite/remotes/name.json | 11 + .../remotes/subSchemas.json | 8 + .../JSON-Schema-Test-Suite/test-schema.json | 59 + .../tests/draft2019-08/additionalItems.json | 87 + .../draft2019-08/additionalProperties.json | 133 + .../tests/draft2019-08/allOf.json | 218 + .../tests/draft2019-08/anyOf.json | 189 + .../tests/draft2019-08/boolean_schema.json | 104 + .../tests/draft2019-08/const.json | 170 + .../tests/draft2019-08/contains.json | 95 + .../tests/draft2019-08/default.json | 49 + .../tests/draft2019-08/defs.json | 24 + .../tests/draft2019-08/dependencies.json | 268 + .../tests/draft2019-08/enum.json | 179 + .../tests/draft2019-08/exclusiveMaximum.json | 30 + .../tests/draft2019-08/exclusiveMinimum.json | 30 + .../tests/draft2019-08/if-then-else.json | 188 + .../tests/draft2019-08/items.json | 250 + .../tests/draft2019-08/maxItems.json | 28 + .../tests/draft2019-08/maxLength.json | 33 + .../tests/draft2019-08/maxProperties.json | 38 + .../tests/draft2019-08/maximum.json | 28 + .../tests/draft2019-08/minItems.json | 28 + .../tests/draft2019-08/minLength.json | 33 + .../tests/draft2019-08/minProperties.json | 38 + .../tests/draft2019-08/minimum.json | 59 + .../tests/draft2019-08/multipleOf.json | 60 + .../tests/draft2019-08/not.json | 117 + .../tests/draft2019-08/oneOf.json | 206 + .../tests/draft2019-08/optional/bignum.json | 105 + .../tests/draft2019-08/optional/content.json | 77 + .../optional/ecmascript-regex.json | 13 + .../optional/format/date-time.json | 53 + .../draft2019-08/optional/format/date.json | 23 + .../draft2019-08/optional/format/email.json | 18 + .../optional/format/hostname.json | 33 + .../optional/format/idn-email.json | 18 + .../optional/format/idn-hostname.json | 28 + .../draft2019-08/optional/format/ipv4.json | 33 + .../draft2019-08/optional/format/ipv6.json | 28 + .../optional/format/iri-reference.json | 43 + .../draft2019-08/optional/format/iri.json | 53 + .../optional/format/json-pointer.json | 168 + .../draft2019-08/optional/format/regex.json | 18 + .../format/relative-json-pointer.json | 33 + .../draft2019-08/optional/format/time.json | 23 + .../optional/format/uri-reference.json | 43 + .../optional/format/uri-template.json | 30 + .../draft2019-08/optional/format/uri.json | 103 + .../optional/zeroTerminatedFloats.json | 15 + .../tests/draft2019-08/pattern.json | 34 + .../tests/draft2019-08/patternProperties.json | 151 + .../tests/draft2019-08/properties.json | 167 + .../tests/draft2019-08/propertyNames.json | 78 + .../tests/draft2019-08/ref.json | 443 + .../tests/draft2019-08/refRemote.json | 167 + .../tests/draft2019-08/required.json | 105 + .../tests/draft2019-08/type.json | 464 + .../tests/draft2019-08/uniqueItems.json | 89 + .../tests/draft3/additionalItems.json | 82 + .../tests/draft3/additionalProperties.json | 133 + .../tests/draft3/default.json | 49 + .../tests/draft3/dependencies.json | 118 + .../tests/draft3/disallow.json | 80 + .../tests/draft3/divisibleBy.json | 60 + .../tests/draft3/enum.json | 71 + .../tests/draft3/extends.json | 94 + .../tests/draft3/items.json | 46 + .../tests/draft3/maxItems.json | 28 + .../tests/draft3/maxLength.json | 33 + .../tests/draft3/maximum.json | 42 + .../tests/draft3/minItems.json | 28 + .../tests/draft3/minLength.json | 33 + .../tests/draft3/minimum.json | 73 + .../tests/draft3/optional/bignum.json | 107 + .../tests/draft3/optional/format.json | 227 + .../tests/draft3/optional/jsregex.json | 18 + .../draft3/optional/zeroTerminatedFloats.json | 15 + .../tests/draft3/pattern.json | 34 + .../tests/draft3/patternProperties.json | 115 + .../tests/draft3/properties.json | 97 + .../tests/draft3/ref.json | 192 + .../tests/draft3/refRemote.json | 74 + .../tests/draft3/required.json | 53 + .../tests/draft3/type.json | 489 + .../tests/draft3/uniqueItems.json | 79 + .../tests/draft4/additionalItems.json | 87 + .../tests/draft4/additionalProperties.json | 133 + .../tests/draft4/allOf.json | 185 + .../tests/draft4/anyOf.json | 156 + .../tests/draft4/default.json | 49 + .../tests/draft4/definitions.json | 32 + .../tests/draft4/dependencies.json | 194 + .../tests/draft4/enum.json | 179 + .../tests/draft4/items.json | 195 + .../tests/draft4/maxItems.json | 28 + .../tests/draft4/maxLength.json | 33 + .../tests/draft4/maxProperties.json | 38 + .../tests/draft4/maximum.json | 73 + .../tests/draft4/minItems.json | 28 + .../tests/draft4/minLength.json | 33 + .../tests/draft4/minProperties.json | 38 + .../tests/draft4/minimum.json | 104 + .../tests/draft4/multipleOf.json | 60 + .../tests/draft4/not.json | 96 + .../tests/draft4/oneOf.json | 162 + .../tests/draft4/optional/bignum.json | 107 + .../draft4/optional/ecmascript-regex.json | 13 + .../tests/draft4/optional/format.json | 253 + .../draft4/optional/zeroTerminatedFloats.json | 15 + .../tests/draft4/pattern.json | 34 + .../tests/draft4/patternProperties.json | 120 + .../tests/draft4/properties.json | 136 + .../tests/draft4/ref.json | 411 + .../tests/draft4/refRemote.json | 171 + .../tests/draft4/required.json | 89 + .../tests/draft4/type.json | 464 + .../tests/draft4/uniqueItems.json | 89 + .../tests/draft6/additionalItems.json | 87 + .../tests/draft6/additionalProperties.json | 133 + .../tests/draft6/allOf.json | 218 + .../tests/draft6/anyOf.json | 189 + .../tests/draft6/boolean_schema.json | 104 + .../tests/draft6/const.json | 170 + .../tests/draft6/contains.json | 100 + .../tests/draft6/default.json | 49 + .../tests/draft6/definitions.json | 32 + .../tests/draft6/dependencies.json | 268 + .../tests/draft6/enum.json | 179 + .../tests/draft6/exclusiveMaximum.json | 30 + .../tests/draft6/exclusiveMinimum.json | 30 + .../tests/draft6/items.json | 250 + .../tests/draft6/maxItems.json | 28 + .../tests/draft6/maxLength.json | 33 + .../tests/draft6/maxProperties.json | 38 + .../tests/draft6/maximum.json | 28 + .../tests/draft6/minItems.json | 28 + .../tests/draft6/minLength.json | 33 + .../tests/draft6/minProperties.json | 38 + .../tests/draft6/minimum.json | 59 + .../tests/draft6/multipleOf.json | 60 + .../tests/draft6/not.json | 117 + .../tests/draft6/oneOf.json | 206 + .../tests/draft6/optional/bignum.json | 105 + .../draft6/optional/ecmascript-regex.json | 13 + .../tests/draft6/optional/format.json | 493 + .../draft6/optional/zeroTerminatedFloats.json | 15 + .../tests/draft6/pattern.json | 34 + .../tests/draft6/patternProperties.json | 151 + .../tests/draft6/properties.json | 167 + .../tests/draft6/propertyNames.json | 78 + .../tests/draft6/ref.json | 443 + .../tests/draft6/refRemote.json | 171 + .../tests/draft6/required.json | 105 + .../tests/draft6/type.json | 464 + .../tests/draft6/uniqueItems.json | 89 + .../tests/draft7/additionalItems.json | 87 + .../tests/draft7/additionalProperties.json | 133 + .../tests/draft7/allOf.json | 218 + .../tests/draft7/anyOf.json | 189 + .../tests/draft7/boolean_schema.json | 104 + .../tests/draft7/const.json | 170 + .../tests/draft7/contains.json | 100 + .../tests/draft7/default.json | 49 + .../tests/draft7/definitions.json | 32 + .../tests/draft7/dependencies.json | 268 + .../tests/draft7/enum.json | 179 + .../tests/draft7/exclusiveMaximum.json | 30 + .../tests/draft7/exclusiveMinimum.json | 30 + .../tests/draft7/if-then-else.json | 188 + .../tests/draft7/items.json | 250 + .../tests/draft7/maxItems.json | 28 + .../tests/draft7/maxLength.json | 33 + .../tests/draft7/maxProperties.json | 38 + .../tests/draft7/maximum.json | 28 + .../tests/draft7/minItems.json | 28 + .../tests/draft7/minLength.json | 33 + .../tests/draft7/minProperties.json | 38 + .../tests/draft7/minimum.json | 59 + .../tests/draft7/multipleOf.json | 60 + .../tests/draft7/not.json | 117 + .../tests/draft7/oneOf.json | 206 + .../tests/draft7/optional/bignum.json | 105 + .../tests/draft7/optional/content.json | 77 + .../draft7/optional/ecmascript-regex.json | 13 + .../draft7/optional/format/date-time.json | 53 + .../tests/draft7/optional/format/date.json | 23 + .../tests/draft7/optional/format/email.json | 18 + .../draft7/optional/format/hostname.json | 33 + .../draft7/optional/format/idn-email.json | 18 + .../draft7/optional/format/idn-hostname.json | 28 + .../tests/draft7/optional/format/ipv4.json | 33 + .../tests/draft7/optional/format/ipv6.json | 28 + .../draft7/optional/format/iri-reference.json | 43 + .../tests/draft7/optional/format/iri.json | 53 + .../draft7/optional/format/json-pointer.json | 168 + .../tests/draft7/optional/format/regex.json | 18 + .../format/relative-json-pointer.json | 33 + .../tests/draft7/optional/format/time.json | 23 + .../draft7/optional/format/uri-reference.json | 43 + .../draft7/optional/format/uri-template.json | 30 + .../tests/draft7/optional/format/uri.json | 103 + .../draft7/optional/zeroTerminatedFloats.json | 15 + .../tests/draft7/pattern.json | 34 + .../tests/draft7/patternProperties.json | 151 + .../tests/draft7/properties.json | 167 + .../tests/draft7/propertyNames.json | 78 + .../tests/draft7/ref.json | 443 + .../tests/draft7/refRemote.json | 171 + .../tests/draft7/required.json | 105 + .../tests/draft7/type.json | 464 + .../tests/draft7/uniqueItems.json | 89 + thirdparty/JSON-Schema-Test-Suite/tox.ini | 8 + thirdparty/gtest-1.7.0/CHANGES | 157 + thirdparty/gtest-1.7.0/CMakeLists.txt | 252 + thirdparty/gtest-1.7.0/CONTRIBUTORS | 37 + thirdparty/gtest-1.7.0/LICENSE | 28 + thirdparty/gtest-1.7.0/Makefile.am | 306 + thirdparty/gtest-1.7.0/Makefile.in | 1360 ++ thirdparty/gtest-1.7.0/README | 435 + thirdparty/gtest-1.7.0/aclocal.m4 | 1198 + thirdparty/gtest-1.7.0/build-aux/config.guess | 1530 ++ thirdparty/gtest-1.7.0/build-aux/config.h.in | 69 + thirdparty/gtest-1.7.0/build-aux/config.sub | 1773 ++ thirdparty/gtest-1.7.0/build-aux/depcomp | 688 + thirdparty/gtest-1.7.0/build-aux/install-sh | 527 + thirdparty/gtest-1.7.0/build-aux/ltmain.sh | 9661 ++++++++ thirdparty/gtest-1.7.0/build-aux/missing | 331 + .../gtest-1.7.0/cmake/internal_utils.cmake | 227 + thirdparty/gtest-1.7.0/codegear/gtest.cbproj | 138 + .../gtest-1.7.0/codegear/gtest.groupproj | 54 + thirdparty/gtest-1.7.0/codegear/gtest_all.cc | 38 + thirdparty/gtest-1.7.0/codegear/gtest_link.cc | 40 + .../gtest-1.7.0/codegear/gtest_main.cbproj | 82 + .../codegear/gtest_unittest.cbproj | 88 + thirdparty/gtest-1.7.0/configure | 18222 ++++++++++++++ thirdparty/gtest-1.7.0/configure.ac | 68 + .../gtest-1.7.0/fused-src/gtest/gtest-all.cc | 9592 ++++++++ .../gtest-1.7.0/fused-src/gtest/gtest.h | 20061 ++++++++++++++++ .../gtest-1.7.0/fused-src/gtest/gtest_main.cc | 38 + .../include/gtest/gtest-death-test.h | 294 + .../gtest-1.7.0/include/gtest/gtest-message.h | 250 + .../include/gtest/gtest-param-test.h | 1421 ++ .../include/gtest/gtest-param-test.h.pump | 487 + .../include/gtest/gtest-printers.h | 855 + .../gtest-1.7.0/include/gtest/gtest-spi.h | 232 + .../include/gtest/gtest-test-part.h | 179 + .../include/gtest/gtest-typed-test.h | 259 + thirdparty/gtest-1.7.0/include/gtest/gtest.h | 2291 ++ .../include/gtest/gtest_pred_impl.h | 358 + .../gtest-1.7.0/include/gtest/gtest_prod.h | 58 + .../internal/gtest-death-test-internal.h | 319 + .../include/gtest/internal/gtest-filepath.h | 206 + .../include/gtest/internal/gtest-internal.h | 1158 + .../include/gtest/internal/gtest-linked_ptr.h | 233 + .../internal/gtest-param-util-generated.h | 5143 ++++ .../gtest-param-util-generated.h.pump | 301 + .../include/gtest/internal/gtest-param-util.h | 619 + .../include/gtest/internal/gtest-port.h | 1947 ++ .../include/gtest/internal/gtest-string.h | 167 + .../include/gtest/internal/gtest-tuple.h | 1012 + .../include/gtest/internal/gtest-tuple.h.pump | 339 + .../include/gtest/internal/gtest-type-util.h | 3331 +++ .../gtest/internal/gtest-type-util.h.pump | 297 + thirdparty/gtest-1.7.0/m4/acx_pthread.m4 | 363 + thirdparty/gtest-1.7.0/m4/gtest.m4 | 74 + thirdparty/gtest-1.7.0/m4/libtool.m4 | 8001 ++++++ thirdparty/gtest-1.7.0/m4/ltoptions.m4 | 384 + thirdparty/gtest-1.7.0/m4/ltsugar.m4 | 123 + thirdparty/gtest-1.7.0/m4/ltversion.m4 | 23 + thirdparty/gtest-1.7.0/m4/lt~obsolete.m4 | 98 + thirdparty/gtest-1.7.0/make/Makefile | 82 + thirdparty/gtest-1.7.0/msvc/gtest-md.sln | 45 + thirdparty/gtest-1.7.0/msvc/gtest-md.vcproj | 126 + thirdparty/gtest-1.7.0/msvc/gtest.sln | 45 + thirdparty/gtest-1.7.0/msvc/gtest.vcproj | 126 + .../gtest-1.7.0/msvc/gtest_main-md.vcproj | 129 + thirdparty/gtest-1.7.0/msvc/gtest_main.vcproj | 129 + .../msvc/gtest_prod_test-md.vcproj | 164 + .../gtest-1.7.0/msvc/gtest_prod_test.vcproj | 164 + .../gtest-1.7.0/msvc/gtest_unittest-md.vcproj | 147 + .../gtest-1.7.0/msvc/gtest_unittest.vcproj | 147 + thirdparty/gtest-1.7.0/samples/prime_tables.h | 123 + thirdparty/gtest-1.7.0/samples/sample1.cc | 68 + thirdparty/gtest-1.7.0/samples/sample1.h | 43 + .../gtest-1.7.0/samples/sample10_unittest.cc | 144 + .../gtest-1.7.0/samples/sample1_unittest.cc | 153 + thirdparty/gtest-1.7.0/samples/sample2.cc | 56 + thirdparty/gtest-1.7.0/samples/sample2.h | 85 + .../gtest-1.7.0/samples/sample2_unittest.cc | 109 + thirdparty/gtest-1.7.0/samples/sample3-inl.h | 172 + .../gtest-1.7.0/samples/sample3_unittest.cc | 151 + thirdparty/gtest-1.7.0/samples/sample4.cc | 46 + thirdparty/gtest-1.7.0/samples/sample4.h | 53 + .../gtest-1.7.0/samples/sample4_unittest.cc | 45 + .../gtest-1.7.0/samples/sample5_unittest.cc | 199 + .../gtest-1.7.0/samples/sample6_unittest.cc | 224 + .../gtest-1.7.0/samples/sample7_unittest.cc | 130 + .../gtest-1.7.0/samples/sample8_unittest.cc | 173 + .../gtest-1.7.0/samples/sample9_unittest.cc | 160 + .../gtest-1.7.0/scripts/fuse_gtest_files.py | 250 + .../scripts/gen_gtest_pred_impl.py | 730 + .../gtest-1.7.0/scripts/gtest-config.in | 274 + thirdparty/gtest-1.7.0/scripts/pump.py | 855 + thirdparty/gtest-1.7.0/scripts/test/Makefile | 59 + thirdparty/gtest-1.7.0/src/gtest-all.cc | 48 + .../gtest-1.7.0/src/gtest-death-test.cc | 1344 ++ thirdparty/gtest-1.7.0/src/gtest-filepath.cc | 382 + .../gtest-1.7.0/src/gtest-internal-inl.h | 1218 + thirdparty/gtest-1.7.0/src/gtest-port.cc | 805 + thirdparty/gtest-1.7.0/src/gtest-printers.cc | 363 + thirdparty/gtest-1.7.0/src/gtest-test-part.cc | 110 + .../gtest-1.7.0/src/gtest-typed-test.cc | 110 + thirdparty/gtest-1.7.0/src/gtest.cc | 5015 ++++ thirdparty/gtest-1.7.0/src/gtest_main.cc | 38 + .../test/gtest-death-test_ex_test.cc | 93 + .../gtest-1.7.0/test/gtest-death-test_test.cc | 1367 ++ .../gtest-1.7.0/test/gtest-filepath_test.cc | 680 + .../gtest-1.7.0/test/gtest-linked_ptr_test.cc | 154 + .../gtest-1.7.0/test/gtest-listener_test.cc | 310 + .../gtest-1.7.0/test/gtest-message_test.cc | 159 + .../gtest-1.7.0/test/gtest-options_test.cc | 215 + .../test/gtest-param-test2_test.cc | 65 + .../gtest-1.7.0/test/gtest-param-test_test.cc | 904 + .../gtest-1.7.0/test/gtest-param-test_test.h | 57 + .../gtest-1.7.0/test/gtest-port_test.cc | 1253 + .../gtest-1.7.0/test/gtest-printers_test.cc | 1566 ++ .../gtest-1.7.0/test/gtest-test-part_test.cc | 208 + .../gtest-1.7.0/test/gtest-tuple_test.cc | 320 + .../test/gtest-typed-test2_test.cc | 45 + .../gtest-1.7.0/test/gtest-typed-test_test.cc | 360 + .../gtest-1.7.0/test/gtest-typed-test_test.h | 66 + .../test/gtest-unittest-api_test.cc | 341 + thirdparty/gtest-1.7.0/test/gtest_all_test.cc | 47 + .../test/gtest_break_on_failure_unittest.py | 212 + .../test/gtest_break_on_failure_unittest_.cc | 88 + .../test/gtest_catch_exceptions_test.py | 237 + .../test/gtest_catch_exceptions_test_.cc | 311 + .../gtest-1.7.0/test/gtest_color_test.py | 130 + .../gtest-1.7.0/test/gtest_color_test_.cc | 71 + .../gtest-1.7.0/test/gtest_env_var_test.py | 103 + .../gtest-1.7.0/test/gtest_env_var_test_.cc | 126 + .../test/gtest_environment_test.cc | 192 + .../gtest-1.7.0/test/gtest_filter_unittest.py | 633 + .../test/gtest_filter_unittest_.cc | 140 + .../gtest-1.7.0/test/gtest_help_test.py | 172 + .../gtest-1.7.0/test/gtest_help_test_.cc | 46 + .../test/gtest_list_tests_unittest.py | 207 + .../test/gtest_list_tests_unittest_.cc | 157 + .../gtest-1.7.0/test/gtest_main_unittest.cc | 45 + .../test/gtest_no_test_unittest.cc | 56 + .../gtest-1.7.0/test/gtest_output_test.py | 335 + .../gtest-1.7.0/test/gtest_output_test_.cc | 1034 + .../test/gtest_output_test_golden_lin.txt | 720 + .../test/gtest_pred_impl_unittest.cc | 2427 ++ .../test/gtest_premature_exit_test.cc | 141 + .../gtest-1.7.0/test/gtest_prod_test.cc | 57 + .../gtest-1.7.0/test/gtest_repeat_test.cc | 253 + .../gtest-1.7.0/test/gtest_shuffle_test.py | 325 + .../gtest-1.7.0/test/gtest_shuffle_test_.cc | 103 + .../test/gtest_sole_header_test.cc | 57 + .../gtest-1.7.0/test/gtest_stress_test.cc | 256 + .../gtest-1.7.0/test/gtest_test_utils.py | 320 + .../test/gtest_throw_on_failure_ex_test.cc | 92 + .../test/gtest_throw_on_failure_test.py | 171 + .../test/gtest_throw_on_failure_test_.cc | 72 + .../test/gtest_uninitialized_test.py | 70 + .../test/gtest_uninitialized_test_.cc | 43 + thirdparty/gtest-1.7.0/test/gtest_unittest.cc | 7415 ++++++ .../test/gtest_xml_outfile1_test_.cc | 49 + .../test/gtest_xml_outfile2_test_.cc | 49 + .../test/gtest_xml_outfiles_test.py | 132 + .../test/gtest_xml_output_unittest.py | 307 + .../test/gtest_xml_output_unittest_.cc | 181 + .../gtest-1.7.0/test/gtest_xml_test_utils.py | 194 + thirdparty/gtest-1.7.0/test/production.cc | 36 + thirdparty/gtest-1.7.0/test/production.h | 55 + .../xcode/Config/DebugProject.xcconfig | 30 + .../xcode/Config/FrameworkTarget.xcconfig | 17 + .../gtest-1.7.0/xcode/Config/General.xcconfig | 41 + .../xcode/Config/ReleaseProject.xcconfig | 32 + .../xcode/Config/StaticLibraryTarget.xcconfig | 18 + .../xcode/Config/TestTarget.xcconfig | 8 + .../gtest-1.7.0/xcode/Resources/Info.plist | 30 + .../xcode/Samples/FrameworkSample/Info.plist | 28 + .../WidgetFramework.xcodeproj/project.pbxproj | 457 + .../xcschemes/Test.xcscheme | 59 + .../xcschemes/TestAndBuild.xcscheme | 59 + .../xcschemes/WidgetFramework.xcscheme | 59 + .../xcschemes/WidgetFrameworkTest.xcscheme | 86 + .../xcschemes/xcschememanagement.plist | 52 + .../xcode/Samples/FrameworkSample/runtests.sh | 62 + .../xcode/Samples/FrameworkSample/widget.cc | 63 + .../xcode/Samples/FrameworkSample/widget.h | 59 + .../Samples/FrameworkSample/widget_test.cc | 68 + .../gtest-1.7.0/xcode/Scripts/runtests.sh | 65 + .../xcode/Scripts/versiongenerate.py | 100 + .../xcode/gtest.xcodeproj/project.pbxproj | 1135 + .../xcschemes/Check.xcscheme | 59 + .../xcschemes/Version Info.xcscheme | 59 + .../xcschemes/gtest-framework.xcscheme | 59 + .../xcschemes/gtest-static.xcscheme | 59 + .../xcschemes/gtest_main-static.xcscheme | 59 + .../gtest_unittest-framework.xcscheme | 86 + .../xcschemes/gtest_unittest-static.xcscheme | 86 + .../sample1_unittest-framework.xcscheme | 86 + .../sample1_unittest-static.xcscheme | 86 + .../xcschemes/xcschememanagement.plist | 102 + thirdparty/json11-ec4e452/.gitignore | 12 + thirdparty/json11-ec4e452/CMakeLists.txt | 57 + thirdparty/json11-ec4e452/LICENSE.txt | 19 + thirdparty/json11-ec4e452/Makefile | 15 + thirdparty/json11-ec4e452/README.md | 42 + thirdparty/json11-ec4e452/json11.cpp | 788 + thirdparty/json11-ec4e452/json11.hpp | 232 + thirdparty/json11-ec4e452/json11.pc.in | 9 + thirdparty/json11-ec4e452/test.cpp | 281 + thirdparty/jsoncpp-0.9.4/AUTHORS | 1 + thirdparty/jsoncpp-0.9.4/LICENSE | 55 + thirdparty/jsoncpp-0.9.4/README.md | 210 + .../jsoncpp-0.9.4/include/json/assertions.h | 46 + .../jsoncpp-0.9.4/include/json/autolink.h | 25 + .../jsoncpp-0.9.4/include/json/config.h | 101 + .../jsoncpp-0.9.4/include/json/features.h | 51 + .../jsoncpp-0.9.4/include/json/forwards.h | 37 + thirdparty/jsoncpp-0.9.4/include/json/json.h | 15 + .../jsoncpp-0.9.4/include/json/reader.h | 358 + thirdparty/jsoncpp-0.9.4/include/json/value.h | 781 + .../jsoncpp-0.9.4/include/json/version.h | 14 + .../jsoncpp-0.9.4/include/json/writer.h | 315 + .../src/lib_json/json_reader.cpp | 1835 ++ .../jsoncpp-0.9.4/src/lib_json/json_tool.h | 87 + .../jsoncpp-0.9.4/src/lib_json/json_value.cpp | 1487 ++ .../src/lib_json/json_valueiterator.inl | 154 + .../src/lib_json/json_writer.cpp | 1147 + thirdparty/jsoncpp-0.9.4/version | 1 + thirdparty/nlohmann-json-3.1.2/json.hpp | 17300 +++++++++++++ thirdparty/picojson-1.3.0/.gitignore | 4 + thirdparty/picojson-1.3.0/.gitmodules | 0 thirdparty/picojson-1.3.0/.travis.yml | 8 + thirdparty/picojson-1.3.0/Changes | 25 + thirdparty/picojson-1.3.0/LICENSE | 25 + thirdparty/picojson-1.3.0/Makefile | 26 + thirdparty/picojson-1.3.0/README.mkdn | 195 + .../picojson-1.3.0/examples/github-issues.cc | 110 + .../picojson-1.3.0/examples/iostream.cc | 70 + .../picojson-1.3.0/examples/streaming.cc | 76 + thirdparty/picojson-1.3.0/picojson.h | 1010 + thirdparty/picojson-1.3.0/picotest/picotest.c | 99 + thirdparty/picojson-1.3.0/picotest/picotest.h | 39 + thirdparty/picojson-1.3.0/test.cc | 312 + .../include/rapidjson/allocators.h | 271 + .../include/rapidjson/document.h | 2575 ++ .../include/rapidjson/encodedstream.h | 299 + .../include/rapidjson/encodings.h | 716 + .../include/rapidjson/error/en.h | 74 + .../include/rapidjson/error/error.h | 155 + .../include/rapidjson/filereadstream.h | 99 + .../include/rapidjson/filewritestream.h | 104 + .../rapidjson-1.1.0/include/rapidjson/fwd.h | 151 + .../include/rapidjson/internal/biginteger.h | 290 + .../include/rapidjson/internal/diyfp.h | 258 + .../include/rapidjson/internal/dtoa.h | 245 + .../include/rapidjson/internal/ieee754.h | 78 + .../include/rapidjson/internal/itoa.h | 304 + .../include/rapidjson/internal/meta.h | 181 + .../include/rapidjson/internal/pow10.h | 55 + .../include/rapidjson/internal/regex.h | 701 + .../include/rapidjson/internal/stack.h | 230 + .../include/rapidjson/internal/strfunc.h | 55 + .../include/rapidjson/internal/strtod.h | 269 + .../include/rapidjson/internal/swap.h | 46 + .../include/rapidjson/istreamwrapper.h | 115 + .../include/rapidjson/memorybuffer.h | 70 + .../include/rapidjson/memorystream.h | 71 + .../include/rapidjson/msinttypes/inttypes.h | 316 + .../include/rapidjson/msinttypes/stdint.h | 300 + .../include/rapidjson/ostreamwrapper.h | 81 + .../include/rapidjson/pointer.h | 1358 ++ .../include/rapidjson/prettywriter.h | 255 + .../include/rapidjson/rapidjson.h | 615 + .../include/rapidjson/reader.h | 1879 ++ .../include/rapidjson/schema.h | 2006 ++ .../include/rapidjson/stream.h | 179 + .../include/rapidjson/stringbuffer.h | 117 + .../include/rapidjson/writer.h | 610 + thirdparty/rapidjson-1.1.0/license.txt | 57 + thirdparty/rapidjson-1.1.0/readme.md | 160 + thirdparty/urdl-2013-08-15/COPYING | 4 + thirdparty/urdl-2013-08-15/Jamroot | 55 + thirdparty/urdl-2013-08-15/LICENSE_1_0.txt | 23 + thirdparty/urdl-2013-08-15/README | 4 + thirdparty/urdl-2013-08-15/build/Jamfile | 102 + thirdparty/urdl-2013-08-15/doc/Jamfile | 62 + thirdparty/urdl-2013-08-15/doc/doxy2qbk.pl | 15 + thirdparty/urdl-2013-08-15/doc/index.html | 21 + thirdparty/urdl-2013-08-15/doc/index.xml | 16 + thirdparty/urdl-2013-08-15/doc/reference.dox | 223 + thirdparty/urdl-2013-08-15/doc/reference.qbk | 4535 ++++ thirdparty/urdl-2013-08-15/doc/reference.xsl | 1251 + thirdparty/urdl-2013-08-15/doc/std_dox.txt | 7 + thirdparty/urdl-2013-08-15/doc/urdl.png | Bin 0 -> 2891 bytes thirdparty/urdl-2013-08-15/doc/urdl.qbk | 556 + thirdparty/urdl-2013-08-15/example/Jamfile | 27 + thirdparty/urdl-2013-08-15/example/get1.cpp | 57 + thirdparty/urdl-2013-08-15/example/get2.cpp | 48 + .../urdl-2013-08-15/example/multiget1.cpp | 92 + .../urdl-2013-08-15/example/multiget2.cpp | 74 + .../include/urdl/detail/abi_prefix.hpp | 31 + .../include/urdl/detail/abi_suffix.hpp | 23 + .../include/urdl/detail/config.hpp | 94 + .../include/urdl/detail/connect.hpp | 181 + .../include/urdl/detail/coroutine.hpp | 55 + .../include/urdl/detail/file_read_stream.hpp | 129 + .../include/urdl/detail/handshake.hpp | 279 + .../include/urdl/detail/http_read_stream.hpp | 533 + .../include/urdl/detail/parsers.hpp | 297 + .../include/urdl/detail/scoped_ptr.hpp | 85 + .../urdl-2013-08-15/include/urdl/http.hpp | 594 + .../include/urdl/impl/http.ipp | 158 + .../include/urdl/impl/istreambuf.ipp | 251 + .../include/urdl/impl/option_set.ipp | 106 + .../urdl-2013-08-15/include/urdl/impl/url.ipp | 348 + .../urdl-2013-08-15/include/urdl/istream.hpp | 379 + .../include/urdl/istreambuf.hpp | 216 + .../include/urdl/option_set.hpp | 151 + .../include/urdl/read_stream.hpp | 807 + .../urdl-2013-08-15/include/urdl/url.hpp | 269 + thirdparty/urdl-2013-08-15/src/urdl.cpp | 17 + thirdparty/urdl-2013-08-15/test/Jamfile | 27 + .../urdl-2013-08-15/test/http_server.hpp | 118 + thirdparty/urdl-2013-08-15/test/istream.cpp | 215 + .../urdl-2013-08-15/test/istreambuf.cpp | 88 + .../urdl-2013-08-15/test/option_set.cpp | 99 + .../urdl-2013-08-15/test/read_stream.cpp | 280 + thirdparty/urdl-2013-08-15/test/unit_test.hpp | 39 + thirdparty/urdl-2013-08-15/test/url.cpp | 311 + valgrind.supp | 31 + xcode/valijson.xcodeproj/project.pbxproj | 1584 ++ 644 files changed, 255456 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 .travis.yml create mode 100644 Authors create mode 100644 CMakeLists.txt create mode 100644 Doxyfile create mode 100644 LICENSE create mode 100644 README.md create mode 100644 cmake/CMakeFindExtensions.cmake create mode 100644 cmake/CMakeHelpers.cmake create mode 100644 cmake/FindPoco.cmake create mode 100644 cmake/Findcurlpp.cmake create mode 100644 cmake/README create mode 100644 doc/schema/draft-03.json create mode 100644 doc/schema/draft-04.json create mode 100644 doc/specifications/draft-fge-json-schema-validation-00.txt create mode 100644 doc/specifications/draft-luff-json-hyper-schema-00.txt create mode 100644 doc/specifications/draft-pbryan-zyp-json-ref-03.txt create mode 100644 doc/specifications/draft-zyp-json-schema-03.txt create mode 100644 doc/specifications/draft-zyp-json-schema-04.txt create mode 100644 doc/specifications/rfc3986-uri.txt create mode 100644 doc/specifications/rfc4627-json.txt create mode 100644 doc/specifications/rfc6901-json-pointer.txt create mode 100644 examples/array_iteration_basics.cpp create mode 100644 examples/array_iteration_template_fn.cpp create mode 100644 examples/custom_schema.cpp create mode 100644 examples/external_schema.cpp create mode 100644 examples/json_pointers.cpp create mode 100644 examples/object_iteration.cpp create mode 100644 examples/remote_resolution.cpp create mode 100644 include/compat/optional.hpp create mode 100644 include/valijson/adapters/adapter.hpp create mode 100644 include/valijson/adapters/basic_adapter.hpp create mode 100644 include/valijson/adapters/frozen_value.hpp create mode 100644 include/valijson/adapters/json11_adapter.hpp create mode 100644 include/valijson/adapters/jsoncpp_adapter.hpp create mode 100644 include/valijson/adapters/nlohmann_json_adapter.hpp create mode 100644 include/valijson/adapters/picojson_adapter.hpp create mode 100644 include/valijson/adapters/poco_json_adapter.hpp create mode 100644 include/valijson/adapters/property_tree_adapter.hpp create mode 100644 include/valijson/adapters/qtjson_adapter.hpp create mode 100644 include/valijson/adapters/rapidjson_adapter.hpp create mode 100644 include/valijson/adapters/std_string_adapter.hpp create mode 100644 include/valijson/constraint_builder.hpp create mode 100644 include/valijson/constraints/basic_constraint.hpp create mode 100644 include/valijson/constraints/concrete_constraints.hpp create mode 100644 include/valijson/constraints/constraint.hpp create mode 100644 include/valijson/constraints/constraint_visitor.hpp create mode 100644 include/valijson/internal/custom_allocator.hpp create mode 100644 include/valijson/internal/debug.hpp create mode 100644 include/valijson/internal/json_pointer.hpp create mode 100644 include/valijson/internal/json_reference.hpp create mode 100644 include/valijson/internal/optional.hpp create mode 100644 include/valijson/internal/uri.hpp create mode 100644 include/valijson/schema.hpp create mode 100644 include/valijson/schema_parser.hpp create mode 100644 include/valijson/subschema.hpp create mode 100644 include/valijson/utils/file_utils.hpp create mode 100644 include/valijson/utils/json11_utils.hpp create mode 100644 include/valijson/utils/jsoncpp_utils.hpp create mode 100644 include/valijson/utils/nlohmann_json_utils.hpp create mode 100644 include/valijson/utils/picojson_utils.hpp create mode 100644 include/valijson/utils/poco_json_utils.hpp create mode 100644 include/valijson/utils/property_tree_utils.hpp create mode 100644 include/valijson/utils/qtjson_utils.hpp create mode 100644 include/valijson/utils/rapidjson_utils.hpp create mode 100644 include/valijson/utils/utf8_utils.hpp create mode 100644 include/valijson/validation_results.hpp create mode 100644 include/valijson/validation_visitor.hpp create mode 100644 include/valijson/validator.hpp create mode 100644 tests/data/documents/array_doubles_10_20_30_40.json create mode 100644 tests/data/documents/array_doubles_1_2_3.json create mode 100644 tests/data/documents/array_doubles_1_2_3_4.json create mode 100644 tests/data/documents/array_empty.json create mode 100644 tests/data/documents/array_integers_10_20_30_40.json create mode 100644 tests/data/documents/array_integers_1_2_3.json create mode 100644 tests/data/documents/array_integers_1_2_3_4.json create mode 100644 tests/data/documents/array_strings_10_20_30_40.json create mode 100644 tests/data/documents/array_strings_1_2_3.json create mode 100644 tests/data/documents/array_strings_1_2_3_4.json create mode 100644 tests/data/documents/object_empty.json create mode 100644 tests/data/schemas/allof_integers_and_numbers.schema.json create mode 100644 tests/test_adapter_comparison.cpp create mode 100644 tests/test_fetch_document_callback.cpp create mode 100644 tests/test_json11_adapter.cpp create mode 100644 tests/test_json_pointer.cpp create mode 100644 tests/test_jsoncpp_adapter.cpp create mode 100644 tests/test_nlohmann_json_adapter.cpp create mode 100644 tests/test_picojson_adapter.cpp create mode 100644 tests/test_poco_json_adapter.cpp create mode 100644 tests/test_poly_constraint.cpp create mode 100644 tests/test_property_tree_adapter.cpp create mode 100644 tests/test_qtjson_adapter.cpp create mode 100644 tests/test_rapidjson_adapter.cpp create mode 100644 tests/test_validation_errors.cpp create mode 100644 tests/test_validator.cpp create mode 100644 thirdparty/JSON-Schema-Test-Suite/.gitignore create mode 100644 thirdparty/JSON-Schema-Test-Suite/.travis.yml create mode 100644 thirdparty/JSON-Schema-Test-Suite/LICENSE create mode 100644 thirdparty/JSON-Schema-Test-Suite/README.md create mode 100644 thirdparty/JSON-Schema-Test-Suite/index.js create mode 100644 thirdparty/JSON-Schema-Test-Suite/package.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/remotes/folder/folderInteger.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/remotes/integer.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/remotes/name-defs.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/remotes/name.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/remotes/subSchemas.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/test-schema.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/additionalItems.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/additionalProperties.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/allOf.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/anyOf.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/boolean_schema.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/const.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/contains.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/default.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/defs.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/dependencies.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/enum.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/exclusiveMaximum.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/exclusiveMinimum.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/if-then-else.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/items.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/maxItems.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/maxLength.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/maxProperties.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/maximum.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/minItems.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/minLength.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/minProperties.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/minimum.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/multipleOf.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/not.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/oneOf.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/optional/bignum.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/optional/content.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/optional/ecmascript-regex.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/optional/format/date-time.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/optional/format/date.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/optional/format/email.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/optional/format/hostname.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/optional/format/idn-email.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/optional/format/idn-hostname.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/optional/format/ipv4.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/optional/format/ipv6.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/optional/format/iri-reference.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/optional/format/iri.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/optional/format/json-pointer.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/optional/format/regex.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/optional/format/relative-json-pointer.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/optional/format/time.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/optional/format/uri-reference.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/optional/format/uri-template.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/optional/format/uri.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/optional/zeroTerminatedFloats.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/pattern.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/patternProperties.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/properties.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/propertyNames.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/ref.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/refRemote.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/required.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/type.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft2019-08/uniqueItems.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft3/additionalItems.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft3/additionalProperties.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft3/default.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft3/dependencies.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft3/disallow.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft3/divisibleBy.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft3/enum.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft3/extends.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft3/items.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft3/maxItems.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft3/maxLength.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft3/maximum.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft3/minItems.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft3/minLength.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft3/minimum.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft3/optional/bignum.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft3/optional/format.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft3/optional/jsregex.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft3/optional/zeroTerminatedFloats.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft3/pattern.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft3/patternProperties.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft3/properties.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft3/ref.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft3/refRemote.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft3/required.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft3/type.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft3/uniqueItems.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/additionalItems.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/additionalProperties.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/allOf.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/anyOf.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/default.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/definitions.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/dependencies.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/enum.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/items.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/maxItems.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/maxLength.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/maxProperties.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/maximum.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/minItems.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/minLength.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/minProperties.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/minimum.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/multipleOf.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/not.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/oneOf.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/optional/bignum.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/optional/ecmascript-regex.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/optional/format.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/optional/zeroTerminatedFloats.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/pattern.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/patternProperties.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/properties.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/ref.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/refRemote.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/required.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/type.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft4/uniqueItems.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/additionalItems.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/additionalProperties.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/allOf.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/anyOf.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/boolean_schema.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/const.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/contains.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/default.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/definitions.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/dependencies.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/enum.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/exclusiveMaximum.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/exclusiveMinimum.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/items.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/maxItems.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/maxLength.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/maxProperties.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/maximum.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/minItems.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/minLength.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/minProperties.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/minimum.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/multipleOf.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/not.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/oneOf.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/optional/bignum.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/optional/ecmascript-regex.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/optional/format.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/optional/zeroTerminatedFloats.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/pattern.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/patternProperties.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/properties.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/propertyNames.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/ref.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/refRemote.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/required.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/type.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft6/uniqueItems.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/additionalItems.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/additionalProperties.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/allOf.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/anyOf.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/boolean_schema.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/const.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/contains.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/default.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/definitions.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/dependencies.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/enum.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/exclusiveMaximum.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/exclusiveMinimum.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/if-then-else.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/items.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/maxItems.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/maxLength.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/maxProperties.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/maximum.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/minItems.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/minLength.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/minProperties.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/minimum.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/multipleOf.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/not.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/oneOf.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/optional/bignum.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/optional/content.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/optional/ecmascript-regex.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/optional/format/date-time.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/optional/format/date.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/optional/format/email.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/optional/format/hostname.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/optional/format/idn-email.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/optional/format/idn-hostname.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/optional/format/ipv4.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/optional/format/ipv6.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/optional/format/iri-reference.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/optional/format/iri.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/optional/format/json-pointer.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/optional/format/regex.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/optional/format/relative-json-pointer.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/optional/format/time.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/optional/format/uri-reference.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/optional/format/uri-template.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/optional/format/uri.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/optional/zeroTerminatedFloats.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/pattern.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/patternProperties.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/properties.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/propertyNames.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/ref.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/refRemote.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/required.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/type.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tests/draft7/uniqueItems.json create mode 100644 thirdparty/JSON-Schema-Test-Suite/tox.ini create mode 100644 thirdparty/gtest-1.7.0/CHANGES create mode 100644 thirdparty/gtest-1.7.0/CMakeLists.txt create mode 100644 thirdparty/gtest-1.7.0/CONTRIBUTORS create mode 100644 thirdparty/gtest-1.7.0/LICENSE create mode 100644 thirdparty/gtest-1.7.0/Makefile.am create mode 100644 thirdparty/gtest-1.7.0/Makefile.in create mode 100644 thirdparty/gtest-1.7.0/README create mode 100644 thirdparty/gtest-1.7.0/aclocal.m4 create mode 100644 thirdparty/gtest-1.7.0/build-aux/config.guess create mode 100644 thirdparty/gtest-1.7.0/build-aux/config.h.in create mode 100644 thirdparty/gtest-1.7.0/build-aux/config.sub create mode 100644 thirdparty/gtest-1.7.0/build-aux/depcomp create mode 100644 thirdparty/gtest-1.7.0/build-aux/install-sh create mode 100644 thirdparty/gtest-1.7.0/build-aux/ltmain.sh create mode 100644 thirdparty/gtest-1.7.0/build-aux/missing create mode 100644 thirdparty/gtest-1.7.0/cmake/internal_utils.cmake create mode 100644 thirdparty/gtest-1.7.0/codegear/gtest.cbproj create mode 100644 thirdparty/gtest-1.7.0/codegear/gtest.groupproj create mode 100644 thirdparty/gtest-1.7.0/codegear/gtest_all.cc create mode 100644 thirdparty/gtest-1.7.0/codegear/gtest_link.cc create mode 100644 thirdparty/gtest-1.7.0/codegear/gtest_main.cbproj create mode 100644 thirdparty/gtest-1.7.0/codegear/gtest_unittest.cbproj create mode 100644 thirdparty/gtest-1.7.0/configure create mode 100644 thirdparty/gtest-1.7.0/configure.ac create mode 100644 thirdparty/gtest-1.7.0/fused-src/gtest/gtest-all.cc create mode 100644 thirdparty/gtest-1.7.0/fused-src/gtest/gtest.h create mode 100644 thirdparty/gtest-1.7.0/fused-src/gtest/gtest_main.cc create mode 100644 thirdparty/gtest-1.7.0/include/gtest/gtest-death-test.h create mode 100644 thirdparty/gtest-1.7.0/include/gtest/gtest-message.h create mode 100644 thirdparty/gtest-1.7.0/include/gtest/gtest-param-test.h create mode 100644 thirdparty/gtest-1.7.0/include/gtest/gtest-param-test.h.pump create mode 100644 thirdparty/gtest-1.7.0/include/gtest/gtest-printers.h create mode 100644 thirdparty/gtest-1.7.0/include/gtest/gtest-spi.h create mode 100644 thirdparty/gtest-1.7.0/include/gtest/gtest-test-part.h create mode 100644 thirdparty/gtest-1.7.0/include/gtest/gtest-typed-test.h create mode 100644 thirdparty/gtest-1.7.0/include/gtest/gtest.h create mode 100644 thirdparty/gtest-1.7.0/include/gtest/gtest_pred_impl.h create mode 100644 thirdparty/gtest-1.7.0/include/gtest/gtest_prod.h create mode 100644 thirdparty/gtest-1.7.0/include/gtest/internal/gtest-death-test-internal.h create mode 100644 thirdparty/gtest-1.7.0/include/gtest/internal/gtest-filepath.h create mode 100644 thirdparty/gtest-1.7.0/include/gtest/internal/gtest-internal.h create mode 100644 thirdparty/gtest-1.7.0/include/gtest/internal/gtest-linked_ptr.h create mode 100644 thirdparty/gtest-1.7.0/include/gtest/internal/gtest-param-util-generated.h create mode 100644 thirdparty/gtest-1.7.0/include/gtest/internal/gtest-param-util-generated.h.pump create mode 100644 thirdparty/gtest-1.7.0/include/gtest/internal/gtest-param-util.h create mode 100644 thirdparty/gtest-1.7.0/include/gtest/internal/gtest-port.h create mode 100644 thirdparty/gtest-1.7.0/include/gtest/internal/gtest-string.h create mode 100644 thirdparty/gtest-1.7.0/include/gtest/internal/gtest-tuple.h create mode 100644 thirdparty/gtest-1.7.0/include/gtest/internal/gtest-tuple.h.pump create mode 100644 thirdparty/gtest-1.7.0/include/gtest/internal/gtest-type-util.h create mode 100644 thirdparty/gtest-1.7.0/include/gtest/internal/gtest-type-util.h.pump create mode 100644 thirdparty/gtest-1.7.0/m4/acx_pthread.m4 create mode 100644 thirdparty/gtest-1.7.0/m4/gtest.m4 create mode 100644 thirdparty/gtest-1.7.0/m4/libtool.m4 create mode 100644 thirdparty/gtest-1.7.0/m4/ltoptions.m4 create mode 100644 thirdparty/gtest-1.7.0/m4/ltsugar.m4 create mode 100644 thirdparty/gtest-1.7.0/m4/ltversion.m4 create mode 100644 thirdparty/gtest-1.7.0/m4/lt~obsolete.m4 create mode 100644 thirdparty/gtest-1.7.0/make/Makefile create mode 100644 thirdparty/gtest-1.7.0/msvc/gtest-md.sln create mode 100644 thirdparty/gtest-1.7.0/msvc/gtest-md.vcproj create mode 100644 thirdparty/gtest-1.7.0/msvc/gtest.sln create mode 100644 thirdparty/gtest-1.7.0/msvc/gtest.vcproj create mode 100644 thirdparty/gtest-1.7.0/msvc/gtest_main-md.vcproj create mode 100644 thirdparty/gtest-1.7.0/msvc/gtest_main.vcproj create mode 100644 thirdparty/gtest-1.7.0/msvc/gtest_prod_test-md.vcproj create mode 100644 thirdparty/gtest-1.7.0/msvc/gtest_prod_test.vcproj create mode 100644 thirdparty/gtest-1.7.0/msvc/gtest_unittest-md.vcproj create mode 100644 thirdparty/gtest-1.7.0/msvc/gtest_unittest.vcproj create mode 100644 thirdparty/gtest-1.7.0/samples/prime_tables.h create mode 100644 thirdparty/gtest-1.7.0/samples/sample1.cc create mode 100644 thirdparty/gtest-1.7.0/samples/sample1.h create mode 100644 thirdparty/gtest-1.7.0/samples/sample10_unittest.cc create mode 100644 thirdparty/gtest-1.7.0/samples/sample1_unittest.cc create mode 100644 thirdparty/gtest-1.7.0/samples/sample2.cc create mode 100644 thirdparty/gtest-1.7.0/samples/sample2.h create mode 100644 thirdparty/gtest-1.7.0/samples/sample2_unittest.cc create mode 100644 thirdparty/gtest-1.7.0/samples/sample3-inl.h create mode 100644 thirdparty/gtest-1.7.0/samples/sample3_unittest.cc create mode 100644 thirdparty/gtest-1.7.0/samples/sample4.cc create mode 100644 thirdparty/gtest-1.7.0/samples/sample4.h create mode 100644 thirdparty/gtest-1.7.0/samples/sample4_unittest.cc create mode 100644 thirdparty/gtest-1.7.0/samples/sample5_unittest.cc create mode 100644 thirdparty/gtest-1.7.0/samples/sample6_unittest.cc create mode 100644 thirdparty/gtest-1.7.0/samples/sample7_unittest.cc create mode 100644 thirdparty/gtest-1.7.0/samples/sample8_unittest.cc create mode 100644 thirdparty/gtest-1.7.0/samples/sample9_unittest.cc create mode 100644 thirdparty/gtest-1.7.0/scripts/fuse_gtest_files.py create mode 100644 thirdparty/gtest-1.7.0/scripts/gen_gtest_pred_impl.py create mode 100644 thirdparty/gtest-1.7.0/scripts/gtest-config.in create mode 100644 thirdparty/gtest-1.7.0/scripts/pump.py create mode 100644 thirdparty/gtest-1.7.0/scripts/test/Makefile create mode 100644 thirdparty/gtest-1.7.0/src/gtest-all.cc create mode 100644 thirdparty/gtest-1.7.0/src/gtest-death-test.cc create mode 100644 thirdparty/gtest-1.7.0/src/gtest-filepath.cc create mode 100644 thirdparty/gtest-1.7.0/src/gtest-internal-inl.h create mode 100644 thirdparty/gtest-1.7.0/src/gtest-port.cc create mode 100644 thirdparty/gtest-1.7.0/src/gtest-printers.cc create mode 100644 thirdparty/gtest-1.7.0/src/gtest-test-part.cc create mode 100644 thirdparty/gtest-1.7.0/src/gtest-typed-test.cc create mode 100644 thirdparty/gtest-1.7.0/src/gtest.cc create mode 100644 thirdparty/gtest-1.7.0/src/gtest_main.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest-death-test_ex_test.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest-death-test_test.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest-filepath_test.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest-linked_ptr_test.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest-listener_test.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest-message_test.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest-options_test.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest-param-test2_test.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest-param-test_test.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest-param-test_test.h create mode 100644 thirdparty/gtest-1.7.0/test/gtest-port_test.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest-printers_test.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest-test-part_test.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest-tuple_test.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest-typed-test2_test.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest-typed-test_test.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest-typed-test_test.h create mode 100644 thirdparty/gtest-1.7.0/test/gtest-unittest-api_test.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest_all_test.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest_break_on_failure_unittest.py create mode 100644 thirdparty/gtest-1.7.0/test/gtest_break_on_failure_unittest_.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest_catch_exceptions_test.py create mode 100644 thirdparty/gtest-1.7.0/test/gtest_catch_exceptions_test_.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest_color_test.py create mode 100644 thirdparty/gtest-1.7.0/test/gtest_color_test_.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest_env_var_test.py create mode 100644 thirdparty/gtest-1.7.0/test/gtest_env_var_test_.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest_environment_test.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest_filter_unittest.py create mode 100644 thirdparty/gtest-1.7.0/test/gtest_filter_unittest_.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest_help_test.py create mode 100644 thirdparty/gtest-1.7.0/test/gtest_help_test_.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest_list_tests_unittest.py create mode 100644 thirdparty/gtest-1.7.0/test/gtest_list_tests_unittest_.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest_main_unittest.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest_no_test_unittest.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest_output_test.py create mode 100644 thirdparty/gtest-1.7.0/test/gtest_output_test_.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest_output_test_golden_lin.txt create mode 100644 thirdparty/gtest-1.7.0/test/gtest_pred_impl_unittest.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest_premature_exit_test.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest_prod_test.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest_repeat_test.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest_shuffle_test.py create mode 100644 thirdparty/gtest-1.7.0/test/gtest_shuffle_test_.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest_sole_header_test.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest_stress_test.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest_test_utils.py create mode 100644 thirdparty/gtest-1.7.0/test/gtest_throw_on_failure_ex_test.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest_throw_on_failure_test.py create mode 100644 thirdparty/gtest-1.7.0/test/gtest_throw_on_failure_test_.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest_uninitialized_test.py create mode 100644 thirdparty/gtest-1.7.0/test/gtest_uninitialized_test_.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest_unittest.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest_xml_outfile1_test_.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest_xml_outfile2_test_.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest_xml_outfiles_test.py create mode 100644 thirdparty/gtest-1.7.0/test/gtest_xml_output_unittest.py create mode 100644 thirdparty/gtest-1.7.0/test/gtest_xml_output_unittest_.cc create mode 100644 thirdparty/gtest-1.7.0/test/gtest_xml_test_utils.py create mode 100644 thirdparty/gtest-1.7.0/test/production.cc create mode 100644 thirdparty/gtest-1.7.0/test/production.h create mode 100644 thirdparty/gtest-1.7.0/xcode/Config/DebugProject.xcconfig create mode 100644 thirdparty/gtest-1.7.0/xcode/Config/FrameworkTarget.xcconfig create mode 100644 thirdparty/gtest-1.7.0/xcode/Config/General.xcconfig create mode 100644 thirdparty/gtest-1.7.0/xcode/Config/ReleaseProject.xcconfig create mode 100644 thirdparty/gtest-1.7.0/xcode/Config/StaticLibraryTarget.xcconfig create mode 100644 thirdparty/gtest-1.7.0/xcode/Config/TestTarget.xcconfig create mode 100644 thirdparty/gtest-1.7.0/xcode/Resources/Info.plist create mode 100644 thirdparty/gtest-1.7.0/xcode/Samples/FrameworkSample/Info.plist create mode 100644 thirdparty/gtest-1.7.0/xcode/Samples/FrameworkSample/WidgetFramework.xcodeproj/project.pbxproj create mode 100644 thirdparty/gtest-1.7.0/xcode/Samples/FrameworkSample/WidgetFramework.xcodeproj/xcuserdata/Tristan.xcuserdatad/xcschemes/Test.xcscheme create mode 100644 thirdparty/gtest-1.7.0/xcode/Samples/FrameworkSample/WidgetFramework.xcodeproj/xcuserdata/Tristan.xcuserdatad/xcschemes/TestAndBuild.xcscheme create mode 100644 thirdparty/gtest-1.7.0/xcode/Samples/FrameworkSample/WidgetFramework.xcodeproj/xcuserdata/Tristan.xcuserdatad/xcschemes/WidgetFramework.xcscheme create mode 100644 thirdparty/gtest-1.7.0/xcode/Samples/FrameworkSample/WidgetFramework.xcodeproj/xcuserdata/Tristan.xcuserdatad/xcschemes/WidgetFrameworkTest.xcscheme create mode 100644 thirdparty/gtest-1.7.0/xcode/Samples/FrameworkSample/WidgetFramework.xcodeproj/xcuserdata/Tristan.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 thirdparty/gtest-1.7.0/xcode/Samples/FrameworkSample/runtests.sh create mode 100644 thirdparty/gtest-1.7.0/xcode/Samples/FrameworkSample/widget.cc create mode 100644 thirdparty/gtest-1.7.0/xcode/Samples/FrameworkSample/widget.h create mode 100644 thirdparty/gtest-1.7.0/xcode/Samples/FrameworkSample/widget_test.cc create mode 100644 thirdparty/gtest-1.7.0/xcode/Scripts/runtests.sh create mode 100644 thirdparty/gtest-1.7.0/xcode/Scripts/versiongenerate.py create mode 100644 thirdparty/gtest-1.7.0/xcode/gtest.xcodeproj/project.pbxproj create mode 100644 thirdparty/gtest-1.7.0/xcode/gtest.xcodeproj/xcuserdata/Tristan.xcuserdatad/xcschemes/Check.xcscheme create mode 100644 thirdparty/gtest-1.7.0/xcode/gtest.xcodeproj/xcuserdata/Tristan.xcuserdatad/xcschemes/Version Info.xcscheme create mode 100644 thirdparty/gtest-1.7.0/xcode/gtest.xcodeproj/xcuserdata/Tristan.xcuserdatad/xcschemes/gtest-framework.xcscheme create mode 100644 thirdparty/gtest-1.7.0/xcode/gtest.xcodeproj/xcuserdata/Tristan.xcuserdatad/xcschemes/gtest-static.xcscheme create mode 100644 thirdparty/gtest-1.7.0/xcode/gtest.xcodeproj/xcuserdata/Tristan.xcuserdatad/xcschemes/gtest_main-static.xcscheme create mode 100644 thirdparty/gtest-1.7.0/xcode/gtest.xcodeproj/xcuserdata/Tristan.xcuserdatad/xcschemes/gtest_unittest-framework.xcscheme create mode 100644 thirdparty/gtest-1.7.0/xcode/gtest.xcodeproj/xcuserdata/Tristan.xcuserdatad/xcschemes/gtest_unittest-static.xcscheme create mode 100644 thirdparty/gtest-1.7.0/xcode/gtest.xcodeproj/xcuserdata/Tristan.xcuserdatad/xcschemes/sample1_unittest-framework.xcscheme create mode 100644 thirdparty/gtest-1.7.0/xcode/gtest.xcodeproj/xcuserdata/Tristan.xcuserdatad/xcschemes/sample1_unittest-static.xcscheme create mode 100644 thirdparty/gtest-1.7.0/xcode/gtest.xcodeproj/xcuserdata/Tristan.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100755 thirdparty/json11-ec4e452/.gitignore create mode 100755 thirdparty/json11-ec4e452/CMakeLists.txt create mode 100755 thirdparty/json11-ec4e452/LICENSE.txt create mode 100755 thirdparty/json11-ec4e452/Makefile create mode 100755 thirdparty/json11-ec4e452/README.md create mode 100755 thirdparty/json11-ec4e452/json11.cpp create mode 100755 thirdparty/json11-ec4e452/json11.hpp create mode 100755 thirdparty/json11-ec4e452/json11.pc.in create mode 100755 thirdparty/json11-ec4e452/test.cpp create mode 100644 thirdparty/jsoncpp-0.9.4/AUTHORS create mode 100644 thirdparty/jsoncpp-0.9.4/LICENSE create mode 100644 thirdparty/jsoncpp-0.9.4/README.md create mode 100644 thirdparty/jsoncpp-0.9.4/include/json/assertions.h create mode 100644 thirdparty/jsoncpp-0.9.4/include/json/autolink.h create mode 100644 thirdparty/jsoncpp-0.9.4/include/json/config.h create mode 100644 thirdparty/jsoncpp-0.9.4/include/json/features.h create mode 100644 thirdparty/jsoncpp-0.9.4/include/json/forwards.h create mode 100644 thirdparty/jsoncpp-0.9.4/include/json/json.h create mode 100644 thirdparty/jsoncpp-0.9.4/include/json/reader.h create mode 100644 thirdparty/jsoncpp-0.9.4/include/json/value.h create mode 100644 thirdparty/jsoncpp-0.9.4/include/json/version.h create mode 100644 thirdparty/jsoncpp-0.9.4/include/json/writer.h create mode 100644 thirdparty/jsoncpp-0.9.4/src/lib_json/json_reader.cpp create mode 100644 thirdparty/jsoncpp-0.9.4/src/lib_json/json_tool.h create mode 100644 thirdparty/jsoncpp-0.9.4/src/lib_json/json_value.cpp create mode 100644 thirdparty/jsoncpp-0.9.4/src/lib_json/json_valueiterator.inl create mode 100644 thirdparty/jsoncpp-0.9.4/src/lib_json/json_writer.cpp create mode 100644 thirdparty/jsoncpp-0.9.4/version create mode 100644 thirdparty/nlohmann-json-3.1.2/json.hpp create mode 100644 thirdparty/picojson-1.3.0/.gitignore create mode 100644 thirdparty/picojson-1.3.0/.gitmodules create mode 100644 thirdparty/picojson-1.3.0/.travis.yml create mode 100644 thirdparty/picojson-1.3.0/Changes create mode 100644 thirdparty/picojson-1.3.0/LICENSE create mode 100644 thirdparty/picojson-1.3.0/Makefile create mode 100644 thirdparty/picojson-1.3.0/README.mkdn create mode 100644 thirdparty/picojson-1.3.0/examples/github-issues.cc create mode 100644 thirdparty/picojson-1.3.0/examples/iostream.cc create mode 100644 thirdparty/picojson-1.3.0/examples/streaming.cc create mode 100644 thirdparty/picojson-1.3.0/picojson.h create mode 100644 thirdparty/picojson-1.3.0/picotest/picotest.c create mode 100644 thirdparty/picojson-1.3.0/picotest/picotest.h create mode 100644 thirdparty/picojson-1.3.0/test.cc create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/allocators.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/document.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/encodedstream.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/encodings.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/error/en.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/error/error.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/filereadstream.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/filewritestream.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/fwd.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/internal/biginteger.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/internal/diyfp.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/internal/dtoa.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/internal/ieee754.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/internal/itoa.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/internal/meta.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/internal/pow10.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/internal/regex.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/internal/stack.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/internal/strfunc.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/internal/strtod.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/internal/swap.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/istreamwrapper.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/memorybuffer.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/memorystream.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/msinttypes/inttypes.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/msinttypes/stdint.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/ostreamwrapper.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/pointer.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/prettywriter.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/rapidjson.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/reader.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/schema.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/stream.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/stringbuffer.h create mode 100644 thirdparty/rapidjson-1.1.0/include/rapidjson/writer.h create mode 100644 thirdparty/rapidjson-1.1.0/license.txt create mode 100644 thirdparty/rapidjson-1.1.0/readme.md create mode 100644 thirdparty/urdl-2013-08-15/COPYING create mode 100644 thirdparty/urdl-2013-08-15/Jamroot create mode 100644 thirdparty/urdl-2013-08-15/LICENSE_1_0.txt create mode 100644 thirdparty/urdl-2013-08-15/README create mode 100644 thirdparty/urdl-2013-08-15/build/Jamfile create mode 100644 thirdparty/urdl-2013-08-15/doc/Jamfile create mode 100755 thirdparty/urdl-2013-08-15/doc/doxy2qbk.pl create mode 100644 thirdparty/urdl-2013-08-15/doc/index.html create mode 100644 thirdparty/urdl-2013-08-15/doc/index.xml create mode 100644 thirdparty/urdl-2013-08-15/doc/reference.dox create mode 100644 thirdparty/urdl-2013-08-15/doc/reference.qbk create mode 100644 thirdparty/urdl-2013-08-15/doc/reference.xsl create mode 100644 thirdparty/urdl-2013-08-15/doc/std_dox.txt create mode 100755 thirdparty/urdl-2013-08-15/doc/urdl.png create mode 100644 thirdparty/urdl-2013-08-15/doc/urdl.qbk create mode 100644 thirdparty/urdl-2013-08-15/example/Jamfile create mode 100644 thirdparty/urdl-2013-08-15/example/get1.cpp create mode 100644 thirdparty/urdl-2013-08-15/example/get2.cpp create mode 100644 thirdparty/urdl-2013-08-15/example/multiget1.cpp create mode 100644 thirdparty/urdl-2013-08-15/example/multiget2.cpp create mode 100644 thirdparty/urdl-2013-08-15/include/urdl/detail/abi_prefix.hpp create mode 100644 thirdparty/urdl-2013-08-15/include/urdl/detail/abi_suffix.hpp create mode 100644 thirdparty/urdl-2013-08-15/include/urdl/detail/config.hpp create mode 100644 thirdparty/urdl-2013-08-15/include/urdl/detail/connect.hpp create mode 100644 thirdparty/urdl-2013-08-15/include/urdl/detail/coroutine.hpp create mode 100644 thirdparty/urdl-2013-08-15/include/urdl/detail/file_read_stream.hpp create mode 100644 thirdparty/urdl-2013-08-15/include/urdl/detail/handshake.hpp create mode 100644 thirdparty/urdl-2013-08-15/include/urdl/detail/http_read_stream.hpp create mode 100644 thirdparty/urdl-2013-08-15/include/urdl/detail/parsers.hpp create mode 100644 thirdparty/urdl-2013-08-15/include/urdl/detail/scoped_ptr.hpp create mode 100644 thirdparty/urdl-2013-08-15/include/urdl/http.hpp create mode 100644 thirdparty/urdl-2013-08-15/include/urdl/impl/http.ipp create mode 100644 thirdparty/urdl-2013-08-15/include/urdl/impl/istreambuf.ipp create mode 100644 thirdparty/urdl-2013-08-15/include/urdl/impl/option_set.ipp create mode 100644 thirdparty/urdl-2013-08-15/include/urdl/impl/url.ipp create mode 100644 thirdparty/urdl-2013-08-15/include/urdl/istream.hpp create mode 100644 thirdparty/urdl-2013-08-15/include/urdl/istreambuf.hpp create mode 100644 thirdparty/urdl-2013-08-15/include/urdl/option_set.hpp create mode 100644 thirdparty/urdl-2013-08-15/include/urdl/read_stream.hpp create mode 100644 thirdparty/urdl-2013-08-15/include/urdl/url.hpp create mode 100644 thirdparty/urdl-2013-08-15/src/urdl.cpp create mode 100644 thirdparty/urdl-2013-08-15/test/Jamfile create mode 100644 thirdparty/urdl-2013-08-15/test/http_server.hpp create mode 100644 thirdparty/urdl-2013-08-15/test/istream.cpp create mode 100644 thirdparty/urdl-2013-08-15/test/istreambuf.cpp create mode 100644 thirdparty/urdl-2013-08-15/test/option_set.cpp create mode 100644 thirdparty/urdl-2013-08-15/test/read_stream.cpp create mode 100644 thirdparty/urdl-2013-08-15/test/unit_test.hpp create mode 100644 thirdparty/urdl-2013-08-15/test/url.cpp create mode 100644 valgrind.supp create mode 100644 xcode/valijson.xcodeproj/project.pbxproj diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..086e53c609 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +build +bin +xcode/valijson.xcodeproj/project.xcworkspace +xcode/valijson.xcodeproj/project.xcworkspace/xcuserdata +xcode/valijson.xcodeproj/xcuserdata +doc/html diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..e69de29bb2 diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..d425e247fe --- /dev/null +++ b/.travis.yml @@ -0,0 +1,74 @@ +language: cpp +sudo: required +dist: trusty + +matrix: + include: + - os: linux + compiler: gcc + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-5', 'qtbase5-dev', 'libboost1.55-dev', 'libssl-dev', 'libcurl4-openssl-dev'] + env: + - CXX_COMPILER=g++-5 + - C_COMPILER=gcc-5 + + - os: linux + compiler: clang + addons: + apt: + sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6'] + packages: ['clang-3.6', 'qtbase5-dev', 'libboost1.55-dev', 'libssl-dev', 'libcurl4-openssl-dev'] + env: + - CXX_COMPILER=clang++-3.6 + - C_COMPILER=clang-3.6 + + - os: linux + compiler: clang + addons: + apt: + sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.7'] + packages: ['clang-3.7', 'qtbase5-dev', 'libboost1.55-dev', 'libssl-dev', 'libcurl4-openssl-dev'] + env: + - CXX_COMPILER=clang++-3.7 + - C_COMPILER=clang-3.7 + + - os: linux + compiler: clang + addons: + apt: + sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.8'] + packages: ['clang-3.8', 'libc++-dev', 'libc++abi-dev', 'qtbase5-dev', 'libboost1.55-dev', 'libssl-dev', 'libcurl4-openssl-dev'] + env: + - CXX_COMPILER=clang++-3.8 + - C_COMPILER=clang-3.8 + - CMAKE_CXX_FLAGS="-stdlib=libc++" + - CMAKE_EXE_LINKER_FLAGS="-lc++" + +before_install: + - pushd ~ + - wget https://github.com/pocoproject/poco/archive/poco-1.7.8p2-release.tar.gz + - tar -xf poco-1.7.8p2-release.tar.gz + - cd poco-poco-1.7.8p2-release + - mkdir cmake_build + - cd cmake_build + - export POCO_OPTS="-DENABLE_CRYPTO=off -DENABLE_DATA=off -DENABLE_DATA_MYSQL=off -DENABLE_DATA_ODBC=off -DENABLE_DATA_SQLITE=off" + - export POCO_OPTS="$POCO_OPTS -DENABLE_MONGODB=off -DENABLE_NET=off -DENABLE_NETSSL=off -DENABLE_PAGECOMPILER=off" + - export POCO_OPTS="$POCO_OPTS -DENABLE_PAGECOMPILER_FILE2PAGE=off -DENABLE_PDF=off -DENABLE_UTIL=off -DENABLE_XML=off -DENABLE_ZIP=off" + - cmake -D CMAKE_CXX_COMPILER=`which ${CXX_COMPILER}` -DCMAKE_CXX_FLAGS="$CMAKE_CXX_FLAGS" -DCMAKE_EXE_LINKER_FLAGS="$CMAKE_EXE_LINKER_FLAGS" $POCO_OPTS .. + - sudo make install + - wget -O curlpp-0.8.1.tar.gz https://github.com/jpbarrette/curlpp/archive/v0.8.1.tar.gz + - tar -xf curlpp-0.8.1.tar.gz + - cd curlpp-0.8.1 + - mkdir cmake_build + - cd cmake_build + - cmake -D CMAKE_CXX_COMPILER=`which ${CXX_COMPILER}` -DCMAKE_CXX_FLAGS="$CMAKE_CXX_FLAGS" -DCMAKE_EXE_LINKER_FLAGS="$CMAKE_EXE_LINKER_FLAGS" .. + - sudo make install + - popd + +script: + - mkdir build && cd build + - cmake -D CMAKE_CXX_FLAGS="$CMAKE_CXX_FLAGS" -D CMAKE_EXE_LINKER_FLAGS="$CMAKE_EXE_LINKER_FLAGS" -D CMAKE_CXX_COMPILER="$CXX_COMPILER" -D CMAKE_C_COMPILER="$C_COMPILER" .. + - VERBOSE=1 make + - ./test_suite diff --git a/Authors b/Authors new file mode 100644 index 0000000000..a6b025b96a --- /dev/null +++ b/Authors @@ -0,0 +1,35 @@ +Tristan Penman, tristan@tristanpenman.com + Core library development + +James Jensen, jjensen@akamai.com, changes Copyright (c) 2016 Akamai Technologies + Polymorphic constraint support + +Tengiz Sharafiev, btolfa+github@gmail.com + Adapter for nlohmann/json parser library + +hotwatermorning (github username), hotwatermorning@gmail.com + Adapter for json11 parser library + +shsfre09 (github username), + Adapter for picojson parser library + +Michael Smith, michael.smith@puppetlabs.com + Memory management improvements for RapidJson parser library + +Richard Clamp, richardc@unixbeard.net + Boost-related fixes + +Lars Immisch, lars@ibp.de + noboost branch + +Nicolas Witczak, n_witczak@yahoo.com + C++11 improvements + +Martin Robinson, 0x4d52@gmail.com + Bug fixes + +Pierre Lamot, pierre.lamot@yahoo.fr + Adapter for Qt JSON parser + +drewxa (github username), bo.ro.da@mail.ru + Adapter for Poco JSON parser diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000..7a0ba3645d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,163 @@ +cmake_minimum_required(VERSION 3.1) +project(valijson) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +option(valijson_INSTALL_HEADERS "Install valijson headers." FALSE) +option(valijson_BUILD_EXAMPLES "Build valijson examples." FALSE) +option(valijson_BUILD_TESTS "Build valijson test suite." TRUE) +option(valijson_EXCLUDE_BOOST "Exclude Boost when building test suite." FALSE) + +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") + +if(valijson_INSTALL_HEADERS) + install(DIRECTORY include/ DESTINATION include) +endif() + +if(NOT valijson_BUILD_TESTS AND NOT valijson_BUILD_EXAMPLES) + return() +endif() + +SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0") + +include(CheckCXXCompilerFlag) +CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) +if(COMPILER_SUPPORTS_CXX11) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +else() + message(FATAL_ERROR "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") +endif() + +find_package(curlpp) +find_package(Poco OPTIONAL_COMPONENTS JSON) +find_package(Qt5Core) + +# jsoncpp library +add_library(jsoncpp + thirdparty/jsoncpp-0.9.4/src/lib_json/json_reader.cpp + thirdparty/jsoncpp-0.9.4/src/lib_json/json_value.cpp + thirdparty/jsoncpp-0.9.4/src/lib_json/json_writer.cpp +) + +target_include_directories(jsoncpp SYSTEM PRIVATE thirdparty/jsoncpp-0.9.4/include) +set_target_properties(jsoncpp PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/thirdparty/jsoncpp-0.9.4) + +add_library(json11 + thirdparty/json11-ec4e452/json11.cpp +) + +target_include_directories(json11 SYSTEM PRIVATE thirdparty/json11-ec4e452) +set_target_properties(json11 PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/thirdparty/json11-ec4e452) + +# Not all of these are required for examples build it doesn't hurt to include them +include_directories(include SYSTEM + thirdparty/gtest-1.7.0/include + thirdparty/json11-ec4e452 + thirdparty/jsoncpp-0.9.4/include + thirdparty/rapidjson-1.1.0/include + thirdparty/picojson-1.3.0 + thirdparty/nlohmann-json-3.1.2 + ) + +if(valijson_BUILD_TESTS) + if(NOT valijson_EXCLUDE_BOOST) + find_package(Boost) + endif() + + # Build local gtest + set(gtest_force_shared_crt ON) + add_subdirectory(thirdparty/gtest-1.7.0) + + set(TEST_SOURCES + tests/test_adapter_comparison.cpp + tests/test_fetch_document_callback.cpp + tests/test_json_pointer.cpp + tests/test_json11_adapter.cpp + tests/test_jsoncpp_adapter.cpp + tests/test_nlohmann_json_adapter.cpp + tests/test_rapidjson_adapter.cpp + tests/test_picojson_adapter.cpp + tests/test_poly_constraint.cpp + tests/test_validation_errors.cpp + tests/test_validator.cpp + ) + + # Unit tests executable + add_executable(test_suite ${TEST_SOURCES}) + + # Definition for using picojson + set_target_properties(test_suite PROPERTIES COMPILE_DEFINITIONS "PICOJSON_USE_INT64") + + set(TEST_LIBS gtest gtest_main jsoncpp json11) + + if(Boost_FOUND) + include_directories(${Boost_INCLUDE_DIRS}) + list(APPEND TEST_SOURCES tests/test_property_tree_adapter.cpp) + add_definitions(-DBOOST_ALL_DYN_LINK) + set(Boost_USE_STATIC_LIBS OFF) + set(Boost_USE_MULTITHREADED ON) + set(Boost_USE_STATIC_RUNTIME OFF) + target_compile_definitions(test_suite PRIVATE "VALIJSON_BUILD_PROPERTY_TREE_ADAPTER") + endif() + + if(Poco_FOUND) + include_directories(${Poco_INCLUDE_DIRS}) + list(APPEND TEST_SOURCES tests/test_poco_json_adapter.cpp) + list(APPEND TEST_LIBS ${Poco_Foundation_LIBRARIES} ${Poco_JSON_LIBRARIES}) + target_compile_definitions(test_suite PRIVATE "VALIJSON_BUILD_POCO_ADAPTER") + endif() + + if(Qt5Core_FOUND) + include_directories(${Qt5Core_INCLUDE_DIRS}) + list(APPEND TEST_SOURCES tests/test_qtjson_adapter.cpp) + list(APPEND TEST_LIBS Qt5::Core) + target_compile_definitions(test_suite PRIVATE "VALIJSON_BUILD_QT_ADAPTER") + endif() + + target_link_libraries(test_suite ${TEST_LIBS} ${Boost_LIBRARIES}) +endif() + +if(valijson_BUILD_EXAMPLES) + include_directories(SYSTEM) + + add_executable(custom_schema + examples/custom_schema.cpp + ) + + add_executable(external_schema + examples/external_schema.cpp + ) + + add_executable(array_iteration_basics + examples/array_iteration_basics.cpp + ) + + add_executable(array_iteration_template_fn + examples/array_iteration_template_fn.cpp + ) + + add_executable(object_iteration + examples/object_iteration.cpp + ) + + add_executable(json_pointers + examples/json_pointers.cpp + ) + + if(curlpp_FOUND) + include_directories(${curlpp_INCLUDE_DIR}) + + add_executable(remote_resolution + examples/remote_resolution.cpp + ) + + target_link_libraries(remote_resolution curl ${curlpp_LIBRARIES}) + endif() + + target_link_libraries(custom_schema ${Boost_LIBRARIES}) + target_link_libraries(external_schema ${Boost_LIBRARIES}) + target_link_libraries(array_iteration_basics jsoncpp) + target_link_libraries(array_iteration_template_fn jsoncpp) + target_link_libraries(object_iteration jsoncpp) + target_link_libraries(json_pointers) +endif() diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 0000000000..88996c5b1e --- /dev/null +++ b/Doxyfile @@ -0,0 +1,2362 @@ +# Doxyfile 1.8.9.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "Valijson" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = doc + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = NO + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = YES + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = YES + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = YES + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = YES + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = NO + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. +# Note: If this tag is empty the current directory is searched. + +INPUT = README.md include + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank the +# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, +# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, +# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, +# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, +# *.qsf, *.as and *.js. + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = README.md + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /