diff --git a/Help/manual/cmake-server.7.rst b/Help/manual/cmake-server.7.rst index 61d6896cd..b8a425c82 100644 --- a/Help/manual/cmake-server.7.rst +++ b/Help/manual/cmake-server.7.rst @@ -248,3 +248,129 @@ which will result in a response type "reply":: ]== CMake Server ==] indicating that the server is ready for action. + + +Type "globalSettings" +^^^^^^^^^^^^^^^^^^^^^ + +This request can be sent after the initial handshake. It will return a +JSON structure with information on cmake state. + +Example:: + + [== CMake Server ==[ + {"type":"globalSettings"} + ]== CMake Server ==] + +which will result in a response type "reply":: + + [== CMake Server ==[ + { + "buildDirectory": "/tmp/test-build", + "capabilities": { + "generators": [ + { + "extraGenerators": [], + "name": "Watcom WMake", + "platformSupport": false, + "toolsetSupport": false + }, + <...> + ], + "serverMode": false, + "version": { + "isDirty": false, + "major": 3, + "minor": 6, + "patch": 20160830, + "string": "3.6.20160830-gd6abad", + "suffix": "gd6abad" + } + }, + "checkSystemVars": false, + "cookie": "", + "extraGenerator": "", + "generator": "Ninja", + "debugOutput": false, + "inReplyTo": "globalSettings", + "sourceDirectory": "/home/code/cmake", + "trace": false, + "traceExpand": false, + "type": "reply", + "warnUninitialized": false, + "warnUnused": false, + "warnUnusedCli": true + } + ]== CMake Server ==] + + +Type "setGlobalSettings" +^^^^^^^^^^^^^^^^^^^^^^^^ + +This request can be sent to change the global settings attributes. Unknown +attributes are going to be ignored. Read-only attributes reported by +"globalSettings" are all capabilities, buildDirectory, generator, +extraGenerator and sourceDirectory. Any attempt to set these will be ignored, +too. + +All other settings will be changed. + +The server will respond with an empty reply message or an error. + +Example:: + + [== CMake Server ==[ + {"type":"setGlobalSettings","debugOutput":true} + ]== CMake Server ==] + +CMake will reply to this with:: + + [== CMake Server ==[ + {"inReplyTo":"setGlobalSettings","type":"reply"} + ]== CMake Server ==] + + +Type "configure" +^^^^^^^^^^^^^^^^ + +This request will configure a project for build. + +To configure a build directory already containing cmake files, it is enough to +set "buildDirectory" via "setGlobalSettings". To create a fresh build directory +you also need to set "currentGenerator" and "sourceDirectory" via "setGlobalSettings" +in addition to "buildDirectory". + +You may a list of strings to "configure" via the "cacheArguments" key. These +strings will be interpreted similar to command line arguments related to +cache handling that are passed to the cmake command line client. + +Example:: + + [== CMake Server ==[ + {"type":"configure", "cacheArguments":["-Dsomething=else"]} + ]== CMake Server ==] + +CMake will reply like this (after reporting progress for some time):: + + [== CMake Server ==[ + {"cookie":"","inReplyTo":"configure","type":"reply"} + ]== CMake Server ==] + + +Type "compute" +^^^^^^^^^^^^^^ + +This requist will generate build system files in the build directory and +is only available after a project was successfully "configure"d. + +Example:: + + [== CMake Server ==[ + {"type":"compute"} + ]== CMake Server ==] + +CMake will reply (after reporting progress information):: + + [== CMake Server ==[ + {"cookie":"","inReplyTo":"compute","type":"reply"} + ]== CMake Server ==] diff --git a/Source/cmServerDictionary.h b/Source/cmServerDictionary.h index 156ade271..fc1b44dbb 100644 --- a/Source/cmServerDictionary.h +++ b/Source/cmServerDictionary.h @@ -16,15 +16,23 @@ // Vocabulary: +static const std::string kCOMPUTE_TYPE = "compute"; +static const std::string kCONFIGURE_TYPE = "configure"; static const std::string kERROR_TYPE = "error"; +static const std::string kGLOBAL_SETTINGS_TYPE = "globalSettings"; static const std::string kHANDSHAKE_TYPE = "handshake"; static const std::string kMESSAGE_TYPE = "message"; static const std::string kPROGRESS_TYPE = "progress"; static const std::string kREPLY_TYPE = "reply"; +static const std::string kSET_GLOBAL_SETTINGS_TYPE = "setGlobalSettings"; static const std::string kSIGNAL_TYPE = "signal"; static const std::string kBUILD_DIRECTORY_KEY = "buildDirectory"; +static const std::string kCACHE_ARGUMENTS_KEY = "cacheArguments"; +static const std::string kCAPABILITIES_KEY = "capabilities"; +static const std::string kCHECK_SYSTEM_VARS_KEY = "checkSystemVars"; static const std::string kCOOKIE_KEY = "cookie"; +static const std::string kDEBUG_OUTPUT_KEY = "debugOutput"; static const std::string kERROR_MESSAGE_KEY = "errorMessage"; static const std::string kEXTRA_GENERATOR_KEY = "extraGenerator"; static const std::string kGENERATOR_KEY = "generator"; @@ -43,7 +51,12 @@ static const std::string kSOURCE_DIRECTORY_KEY = "sourceDirectory"; static const std::string kSUPPORTED_PROTOCOL_VERSIONS = "supportedProtocolVersions"; static const std::string kTITLE_KEY = "title"; +static const std::string kTRACE_EXPAND_KEY = "traceExpand"; +static const std::string kTRACE_KEY = "trace"; static const std::string kTYPE_KEY = "type"; +static const std::string kWARN_UNINITIALIZED_KEY = "warnUninitialized"; +static const std::string kWARN_UNUSED_CLI_KEY = "warnUnusedCli"; +static const std::string kWARN_UNUSED_KEY = "warnUnused"; static const std::string kSTART_MAGIC = "[== CMake Server ==["; static const std::string kEND_MAGIC = "]== CMake Server ==]"; diff --git a/Source/cmServerProtocol.cxx b/Source/cmServerProtocol.cxx index 755de0c04..134edf302 100644 --- a/Source/cmServerProtocol.cxx +++ b/Source/cmServerProtocol.cxx @@ -13,6 +13,7 @@ #include "cmServerProtocol.h" #include "cmExternalMakefileProjectGenerator.h" +#include "cmGlobalGenerator.h" #include "cmServer.h" #include "cmServerDictionary.h" #include "cmSystemTools.h" @@ -279,6 +280,19 @@ const cmServerResponse cmServerProtocol1_0::Process( { assert(this->m_State >= STATE_ACTIVE); + if (request.Type == kCOMPUTE_TYPE) { + return this->ProcessCompute(request); + } + if (request.Type == kCONFIGURE_TYPE) { + return this->ProcessConfigure(request); + } + if (request.Type == kGLOBAL_SETTINGS_TYPE) { + return this->ProcessGlobalSettings(request); + } + if (request.Type == kSET_GLOBAL_SETTINGS_TYPE) { + return this->ProcessSetGlobalSettings(request); + } + return request.ReportError("Unknown command!"); } @@ -286,3 +300,179 @@ bool cmServerProtocol1_0::IsExperimental() const { return true; } + +cmServerResponse cmServerProtocol1_0::ProcessCompute( + const cmServerRequest& request) +{ + if (this->m_State > STATE_CONFIGURED) { + return request.ReportError("This build system was already generated."); + } + if (this->m_State < STATE_CONFIGURED) { + return request.ReportError("This project was not configured yet."); + } + + cmake* cm = this->CMakeInstance(); + int ret = cm->Generate(); + + if (ret < 0) { + return request.ReportError("Failed to compute build system."); + } + m_State = STATE_COMPUTED; + return request.Reply(Json::Value()); +} + +cmServerResponse cmServerProtocol1_0::ProcessConfigure( + const cmServerRequest& request) +{ + if (this->m_State == STATE_INACTIVE) { + return request.ReportError("This instance is inactive."); + } + + // Make sure the types of cacheArguments matches (if given): + std::vector cacheArgs; + bool cacheArgumentsError = false; + const Json::Value passedArgs = request.Data[kCACHE_ARGUMENTS_KEY]; + if (!passedArgs.isNull()) { + if (passedArgs.isString()) { + cacheArgs.push_back(passedArgs.asString()); + } else if (passedArgs.isArray()) { + for (auto i = passedArgs.begin(); i != passedArgs.end(); ++i) { + if (!i->isString()) { + cacheArgumentsError = true; + break; + } + cacheArgs.push_back(i->asString()); + } + } else { + cacheArgumentsError = true; + } + } + if (cacheArgumentsError) { + request.ReportError( + "cacheArguments must be unset, a string or an array of strings."); + } + + cmake* cm = this->CMakeInstance(); + std::string sourceDir = cm->GetHomeDirectory(); + const std::string buildDir = cm->GetHomeOutputDirectory(); + + if (buildDir.empty()) { + return request.ReportError( + "No build directory set via setGlobalSettings."); + } + + if (cm->LoadCache(buildDir)) { + // build directory has been set up before + const char* cachedSourceDir = + cm->GetState()->GetInitializedCacheValue("CMAKE_HOME_DIRECTORY"); + if (!cachedSourceDir) { + return request.ReportError("No CMAKE_HOME_DIRECTORY found in cache."); + } + if (sourceDir.empty()) { + sourceDir = std::string(cachedSourceDir); + cm->SetHomeDirectory(sourceDir); + } + + const char* cachedGenerator = + cm->GetState()->GetInitializedCacheValue("CMAKE_GENERATOR"); + if (cachedGenerator) { + cmGlobalGenerator* gen = cm->GetGlobalGenerator(); + if (gen && gen->GetName() != cachedGenerator) { + return request.ReportError("Configured generator does not match with " + "CMAKE_GENERATOR found in cache."); + } + } + } else { + // build directory has not been set up before + if (sourceDir.empty()) { + return request.ReportError("No sourceDirectory set via " + "setGlobalSettings and no cache found in " + "buildDirectory."); + } + } + + if (cm->AddCMakePaths() != 1) { + return request.ReportError("Failed to set CMake paths."); + } + + if (!cm->SetCacheArgs(cacheArgs)) { + return request.ReportError("cacheArguments could not be set."); + } + + int ret = cm->Configure(); + if (ret < 0) { + return request.ReportError("Configuration failed."); + } + m_State = STATE_CONFIGURED; + return request.Reply(Json::Value()); +} + +cmServerResponse cmServerProtocol1_0::ProcessGlobalSettings( + const cmServerRequest& request) +{ + cmake* cm = this->CMakeInstance(); + Json::Value obj = Json::objectValue; + + // Capabilities information: + obj[kCAPABILITIES_KEY] = cm->ReportCapabilitiesJson(true); + + obj[kDEBUG_OUTPUT_KEY] = cm->GetDebugOutput(); + obj[kTRACE_KEY] = cm->GetTrace(); + obj[kTRACE_EXPAND_KEY] = cm->GetTraceExpand(); + obj[kWARN_UNINITIALIZED_KEY] = cm->GetWarnUninitialized(); + obj[kWARN_UNUSED_KEY] = cm->GetWarnUnused(); + obj[kWARN_UNUSED_CLI_KEY] = cm->GetWarnUnusedCli(); + obj[kCHECK_SYSTEM_VARS_KEY] = cm->GetCheckSystemVars(); + + obj[kSOURCE_DIRECTORY_KEY] = cm->GetHomeDirectory(); + obj[kBUILD_DIRECTORY_KEY] = cm->GetHomeOutputDirectory(); + + // Currently used generator: + cmGlobalGenerator* gen = cm->GetGlobalGenerator(); + obj[kGENERATOR_KEY] = gen ? gen->GetName() : std::string(); + obj[kEXTRA_GENERATOR_KEY] = + gen ? gen->GetExtraGeneratorName() : std::string(); + + return request.Reply(obj); +} + +static void setBool(const cmServerRequest& request, const std::string& key, + std::function setter) +{ + if (request.Data[key].isNull()) { + return; + } + setter(request.Data[key].asBool()); +} + +cmServerResponse cmServerProtocol1_0::ProcessSetGlobalSettings( + const cmServerRequest& request) +{ + const std::vector boolValues = { + kDEBUG_OUTPUT_KEY, kTRACE_KEY, kTRACE_EXPAND_KEY, + kWARN_UNINITIALIZED_KEY, kWARN_UNUSED_KEY, kWARN_UNUSED_CLI_KEY, + kCHECK_SYSTEM_VARS_KEY + }; + for (auto i : boolValues) { + if (!request.Data[i].isNull() && !request.Data[i].isBool()) { + return request.ReportError("\"" + i + + "\" must be unset or a bool value."); + } + } + + cmake* cm = this->CMakeInstance(); + + setBool(request, kDEBUG_OUTPUT_KEY, + [cm](bool e) { cm->SetDebugOutputOn(e); }); + setBool(request, kTRACE_KEY, [cm](bool e) { cm->SetTrace(e); }); + setBool(request, kTRACE_EXPAND_KEY, [cm](bool e) { cm->SetTraceExpand(e); }); + setBool(request, kWARN_UNINITIALIZED_KEY, + [cm](bool e) { cm->SetWarnUninitialized(e); }); + setBool(request, kWARN_UNUSED_KEY, [cm](bool e) { cm->SetWarnUnused(e); }); + setBool(request, kWARN_UNUSED_CLI_KEY, + [cm](bool e) { cm->SetWarnUnusedCli(e); }); + setBool(request, kCHECK_SYSTEM_VARS_KEY, + [cm](bool e) { cm->SetCheckSystemVars(e); }); + + return request.Reply(Json::Value()); +} diff --git a/Source/cmServerProtocol.h b/Source/cmServerProtocol.h index 0383dfe50..e772eca43 100644 --- a/Source/cmServerProtocol.h +++ b/Source/cmServerProtocol.h @@ -13,6 +13,7 @@ #pragma once #include "cmListFileCache.h" +#include "cmake.h" #if defined(CMAKE_BUILD_WITH_CMAKE) #include "cm_jsoncpp_writer.h" @@ -116,10 +117,18 @@ private: bool DoActivate(const cmServerRequest& request, std::string* errorMessage) override; + // Handle requests: + cmServerResponse ProcessCompute(const cmServerRequest& request); + cmServerResponse ProcessConfigure(const cmServerRequest& request); + cmServerResponse ProcessGlobalSettings(const cmServerRequest& request); + cmServerResponse ProcessSetGlobalSettings(const cmServerRequest& request); + enum State { STATE_INACTIVE, - STATE_ACTIVE + STATE_ACTIVE, + STATE_CONFIGURED, + STATE_COMPUTED }; State m_State = STATE_INACTIVE; }; diff --git a/Tests/Server/CMakeLists.txt b/Tests/Server/CMakeLists.txt index 8daf12a5e..03f50429d 100644 --- a/Tests/Server/CMakeLists.txt +++ b/Tests/Server/CMakeLists.txt @@ -19,5 +19,6 @@ macro(do_test bsname file) endmacro() do_test("test_handshake" "tc_handshake.json") +do_test("test_globalSettings" "tc_globalSettings.json") add_executable(Server empty.cpp) diff --git a/Tests/Server/cmakelib.py b/Tests/Server/cmakelib.py index 0f9807898..8beaeefa2 100644 --- a/Tests/Server/cmakelib.py +++ b/Tests/Server/cmakelib.py @@ -106,6 +106,7 @@ def waitForReply(cmakeCommand, originalType, cookie): packet = waitForRawMessage(cmakeCommand) if packet['cookie'] != cookie or packet['type'] != 'reply' or packet['inReplyTo'] != originalType: sys.exit(1) + return packet def waitForError(cmakeCommand, originalType, cookie, message): packet = waitForRawMessage(cmakeCommand) @@ -117,10 +118,71 @@ def waitForProgress(cmakeCommand, originalType, cookie, current, message): if packet['cookie'] != cookie or packet['type'] != 'progress' or packet['inReplyTo'] != originalType or packet['progressCurrent'] != current or packet['progressMessage'] != message: sys.exit(1) -def handshake(cmakeCommand, major, minor): +def handshake(cmakeCommand, major, minor, source, build, generator, extraGenerator): version = { 'major': major } if minor >= 0: version['minor'] = minor - writePayload(cmakeCommand, { 'type': 'handshake', 'protocolVersion': version, 'cookie': 'TEST_HANDSHAKE' }) + writePayload(cmakeCommand, { 'type': 'handshake', 'protocolVersion': version, + 'cookie': 'TEST_HANDSHAKE', 'sourceDirectory': source, 'buildDirectory': build, + 'generator': generator, 'extraGenerator': extraGenerator }) waitForReply(cmakeCommand, 'handshake', 'TEST_HANDSHAKE') + +def validateGlobalSettings(cmakeCommand, cmakeCommandPath, data): + packet = waitForReply(cmakeCommand, 'globalSettings', '') + + capabilities = packet['capabilities'] + + # validate version: + cmakeoutput = subprocess.check_output([ cmakeCommandPath, "--version" ], universal_newlines=True) + cmakeVersion = cmakeoutput.splitlines()[0][14:] + + version = capabilities['version'] + versionString = version['string'] + vs = str(version['major']) + '.' + str(version['minor']) + '.' + str(version['patch']) + if (versionString != vs and not versionString.startswith(vs + '-')): + sys.exit(1) + if (versionString != cmakeVersion): + sys.exit(1) + + # validate generators: + generatorObjects = capabilities['generators'] + + cmakeoutput = subprocess.check_output([ cmakeCommandPath, "--help" ], universal_newlines=True) + index = cmakeoutput.index('\nGenerators\n\n') + cmakeGenerators = [] + for line in cmakeoutput[index + 12:].splitlines(): + if not line.startswith(' '): + continue + if line.startswith(' '): + continue + equalPos = line.find('=') + tmp = '' + if (equalPos > 0): + tmp = line[2:equalPos].strip() + else: + tmp = line.strip() + if tmp.endswith(" [arch]"): + tmp = tmp[0:len(tmp) - 7] + if (len(tmp) > 0) and (" - " not in tmp) and (tmp != 'KDevelop3'): + cmakeGenerators.append(tmp) + + generators = [] + for genObj in generatorObjects: + generators.append(genObj['name']) + + generators.sort() + cmakeGenerators.sort() + + for gen in cmakeGenerators: + if (not gen in generators): + sys.exit(1) + + gen = packet['generator'] + if (gen != '' and not (gen in generators)): + sys.exit(1) + + for i in data: + print("Validating", i) + if (packet[i] != data[i]): + sys.exit(1) diff --git a/Tests/Server/server-test.py b/Tests/Server/server-test.py index e0a3b3b6e..d2bf92ebb 100644 --- a/Tests/Server/server-test.py +++ b/Tests/Server/server-test.py @@ -68,9 +68,25 @@ for obj in testData: if debug: print("Doing handshake:", json.dumps(data)) major = -1 minor = -1 + generator = 'Ninja' + extraGenerator = 'CodeBlocks' + sourceDirectory = sourceDir + buildDirectory = buildDir if 'major' in data: major = data['major'] if 'minor' in data: minor = data['minor'] - cmakelib.handshake(proc, major, minor) + if 'buildDirectory' in data: buildDirectory = data['buildDirectory'] + if 'sourceDirectory' in data: sourceDirectory = data['sourceDirectory'] + if 'generator' in data: generator = data['generator'] + if 'extraGenerator' in data: extraGenerator = data['extraGenerator'] + cmakelib.handshake(proc, major, minor, sourceDirectory, buildDirectory, + generator, extraGenerator) + elif 'validateGlobalSettings' in obj: + data = obj['validateGlobalSettings'] + if not 'buildDirectory' in data: data['buildDirectory'] = buildDir + if not 'sourceDirectory' in data: data['sourceDirectory'] = sourceDir + if not 'generator' in data: data['generator'] = 'Ninja' + if not 'extraGenerator' in data: data['extraGenerator'] = 'CodeBlocks' + cmakelib.validateGlobalSettings(proc, cmakeCommand, data) elif 'message' in obj: print("MESSAGE:", obj["message"]) else: diff --git a/Tests/Server/tc_globalSettings.json b/Tests/Server/tc_globalSettings.json new file mode 100644 index 000000000..d72fb41ac --- /dev/null +++ b/Tests/Server/tc_globalSettings.json @@ -0,0 +1,140 @@ +[ +{ "message": "Testing globalSettings" }, + +{ "handshake": {"major": 1} }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, + + + +{ "message": "Change settings:" }, + +{ "send": { "type": "setGlobalSettings", "warnUnused": true } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": true, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, + +{ "send": { "type": "setGlobalSettings", "warnUnused": false } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, + +{ "send": { "type": "setGlobalSettings", "debugOutput": true } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": true, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, + +{ "send": { "type": "setGlobalSettings", "debugOutput": false } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, + +{ "send": { "type": "setGlobalSettings", "warnUninitialized": true } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": true, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, + +{ "send": { "type": "setGlobalSettings", "warnUninitialized": false } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, + +{ "send": { "type": "setGlobalSettings", "traceExpand": true } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": true, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, + +{ "send": { "type": "setGlobalSettings", "traceExpand": false } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, + + + +{ "send": { "type": "setGlobalSettings", "trace": true } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": true, "warnUnusedCli": true, "checkSystemVars": false } }, + +{ "send": { "type": "setGlobalSettings", "trace": false } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, + +{ "send": { "type": "setGlobalSettings", "warnUnusedCli": false } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": false, "checkSystemVars": false } }, + +{ "send": { "type": "setGlobalSettings", "warnUnusedCli": true } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, + +{ "send": { "type": "setGlobalSettings", "checkSystemVars": true } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": true } }, + +{ "send": { "type": "setGlobalSettings", "checkSystemVars": false } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, + +{ "send": { "type": "setGlobalSettings", "warnUnused": true, "debugOutput": true, "warnUninitialized": true, "traceExpand": true, "trace": true, "warnUnusedCli": false, "checkSystemVars": true } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": true, "debugOutput": true, "warnUninitialized": true, "traceExpand": true, "trace": true, "warnUnusedCli": false, "checkSystemVars": true } }, + +{ "message": "Ignore unknown/readonly" }, + +{ "send": { "type": "setGlobalSettings", "unknownKey": "unknownValue", "extraGenerator": "XXX", "generator": "YYY", "sourceDirectory": "/tmp/source", "buildDirectory": "/tmp/build" } }, +{ "reply": { "type": "setGlobalSettings" } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": true, "debugOutput": true, "warnUninitialized": true, "traceExpand": true, "trace": true, "warnUnusedCli": false, "checkSystemVars": true } }, + +{ "message": "Error paths:" }, + +{ "send": { "type": "setGlobalSettings", "debugOutput": true, "warnUnused": 1 } }, +{ "error": { "type": "setGlobalSettings", "message": "\"warnUnused\" must be unset or a bool value." } }, + +{ "send": { "type": "setGlobalSettings", "warnUnused": true, "debugOutput": 1 } }, +{ "error": { "type": "setGlobalSettings", "message": "\"debugOutput\" must be unset or a bool value." } }, + +{ "send": { "type": "setGlobalSettings", "warnUninitialized": 1, "warnUnused": true, "debugOutput": true } }, +{ "error": { "type": "setGlobalSettings", "message": "\"warnUninitialized\" must be unset or a bool value." } }, + +{ "send": { "type": "setGlobalSettings", "warnUnused": true, "debugOutput": true, "traceExpand": 1 } }, +{ "error": { "type": "setGlobalSettings", "message": "\"traceExpand\" must be unset or a bool value." } }, + +{ "send": { "type": "setGlobalSettings", "debugOutput": true, "trace": 1, "warnUnused": true } }, +{ "error": { "type": "setGlobalSettings", "message": "\"trace\" must be unset or a bool value." } }, + +{ "send": { "type": "setGlobalSettings", "warnUnused": true, "debugOutput": true, "warnUnusedCli": 1.0 } }, +{ "error": { "type": "setGlobalSettings", "message": "\"warnUnusedCli\" must be unset or a bool value." } }, + +{ "send": { "type": "setGlobalSettings", "warnUnused": true, "debugOutput": true, "checkSystemVars": "some string" } }, +{ "error": { "type": "setGlobalSettings", "message": "\"checkSystemVars\" must be unset or a bool value." } }, + +{ "send": { "type": "globalSettings"} }, +{ "validateGlobalSettings": { "warnUnused": true, "debugOutput": true, "warnUninitialized": true, "traceExpand": true, "trace": true, "warnUnusedCli": false, "checkSystemVars": true } }, + +{ "message": "Everything ok." } +]