Skip to content

Commit 24faab2

Browse files
committed
[projmgr] Introduce JSON-RPC server mode
1 parent a2e78cd commit 24faab2

7 files changed

Lines changed: 219 additions & 12 deletions

File tree

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,6 @@
2929
path = external/xerces-c
3030
url = https://github.com/apache/xerces-c.git
3131
ignore = dirty
32+
[submodule "external/json-rpc-cxx"]
33+
path = external/json-rpc-cxx
34+
url = https://github.com/jsonrpcx/json-rpc-cxx.git

external/json-rpc-cxx

Submodule json-rpc-cxx added at a0e195b

tools/projmgr/CMakeLists.txt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ SET(PROJMGR_SOURCE_FILES ProjMgr.cpp ProjMgrKernel.cpp ProjMgrCallback.cpp
1616
ProjMgrYamlEmitter.cpp ProjMgrUtils.cpp ProjMgrExtGenerator.cpp
1717
ProjMgrCbuildBase.cpp ProjMgrCbuild.cpp ProjMgrCbuildIdx.cpp
1818
ProjMgrCbuildGenIdx.cpp ProjMgrCbuildPack.cpp ProjMgrCbuildSet.cpp
19-
ProjMgrCbuildRun.cpp ProjMgrRunDebug.cpp
19+
ProjMgrCbuildRun.cpp ProjMgrRunDebug.cpp ProjMgrRpcServer.cpp
2020
)
2121
SET(PROJMGR_HEADER_FILES ProjMgr.h ProjMgrKernel.h ProjMgrCallback.h
2222
ProjMgrParser.h ProjMgrWorker.h ProjMgrGenerator.h ProjMgrXmlParser.h
2323
ProjMgrYamlParser.h ProjMgrLogger.h ProjMgrYamlSchemaChecker.h
2424
ProjMgrYamlEmitter.h ProjMgrUtils.h ProjMgrExtGenerator.h
25-
ProjMgrCbuildBase.h ProjMgrRunDebug.h
25+
ProjMgrCbuildBase.h ProjMgrRunDebug.h ProjMgrRpcServer.h
2626
)
2727

2828
list(TRANSFORM PROJMGR_SOURCE_FILES PREPEND src/)
@@ -32,8 +32,12 @@ add_library(projmgrlib OBJECT ${PROJMGR_SOURCE_FILES} ${PROJMGR_HEADER_FILES})
3232
target_link_libraries(projmgrlib
3333
PUBLIC
3434
CrossPlatform RteFsUtils RteUtils XmlTree XmlTreeSlim XmlReader
35-
RteModel cxxopts yaml-cpp YmlSchemaChecker)
36-
target_include_directories(projmgrlib PRIVATE include ${PROJECT_BINARY_DIR})
35+
RteModel cxxopts yaml-cpp YmlSchemaChecker
36+
)
37+
target_include_directories(projmgrlib PRIVATE include ${PROJECT_BINARY_DIR}
38+
${CMAKE_SOURCE_DIR}/external/json
39+
${CMAKE_SOURCE_DIR}/external/json-rpc-cxx/include
40+
)
3741

3842
if(SWIG_LIBS)
3943
# projmgr swig

tools/projmgr/include/ProjMgr.h

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020-2024 Arm Limited. All rights reserved.
2+
* Copyright (c) 2020-2025 Arm Limited. All rights reserved.
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -12,6 +12,7 @@
1212
#include "ProjMgrGenerator.h"
1313
#include "ProjMgrYamlEmitter.h"
1414
#include "ProjMgrRunDebug.h"
15+
#include "ProjMgrRpcServer.h"
1516

1617
#include <cxxopts.hpp>
1718

@@ -50,6 +51,11 @@ class ProjMgr {
5051
*/
5152
static int RunProjMgr(int argc, char **argv, char** envp);
5253

54+
/**
55+
* @brief get worker object
56+
* @return reference to m_worker
57+
*/
58+
ProjMgrWorker& GetWorker() { return m_worker; };
5359

5460
protected:
5561
/**
@@ -88,12 +94,6 @@ class ProjMgr {
8894
*/
8995
ProjMgrParser& GetParser() { return m_parser; };
9096

91-
/**
92-
* @brief get worker object
93-
* @return reference to m_worker
94-
*/
95-
ProjMgrWorker& GetWorker() { return m_worker; };
96-
9797
/**
9898
* @brief get generator object
9999
* @return reference to m_generator
@@ -130,6 +130,7 @@ class ProjMgr {
130130
ProjMgrGenerator m_generator;
131131
ProjMgrYamlEmitter m_emitter;
132132
ProjMgrRunDebug m_runDebug;
133+
ProjMgrRpcServer m_rpcServer;
133134

134135
std::string m_csolutionFile;
135136
std::string m_cdefaultFile;
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright (c) 2025 Arm Limited. All rights reserved.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef PROJMGRRPCSERVER_H
8+
#define PROJMGRRPCSERVER_H
9+
10+
#include <map>
11+
#include <string>
12+
13+
/**
14+
* Forward declarations
15+
*/
16+
class ProjMgr;
17+
18+
/**
19+
* @brief projmgr rpc server
20+
*/
21+
class ProjMgrRpcServer {
22+
public:
23+
/**
24+
* @brief class constructor
25+
*/
26+
ProjMgrRpcServer(ProjMgr* manager);
27+
28+
/**
29+
* @brief class destructor
30+
*/
31+
~ProjMgrRpcServer(void);
32+
33+
/**
34+
* @brief run rpc server
35+
* @return true if terminated successfully, otherwise false
36+
*/
37+
bool Run(void);
38+
39+
/**
40+
* @brief get parser object
41+
* @return pointer to m_manager
42+
*/
43+
ProjMgr* GetManager() { return m_manager; };
44+
45+
/**
46+
* @brief set m_shutdown flag
47+
* @param boolean value
48+
*/
49+
void SetShutdown(bool value) { m_shutdown = value; };
50+
51+
/**
52+
* @brief set m_contextLength flag
53+
* @param boolean value
54+
*/
55+
void SetContentLengthHeader(bool value) { m_contextLength = value; };
56+
57+
protected:
58+
ProjMgr* m_manager;
59+
bool m_shutdown = false;
60+
bool m_contextLength = false;
61+
const std::string GetRequestFromStdinWithLength(void);
62+
const std::string GetRequestFromStdin(void);
63+
};
64+
65+
#endif // PROJMGRRPCSERVER_H

tools/projmgr/src/ProjMgr.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Commands:\n\
3535
list packs Print list of used packs from the pack repository\n\
3636
list toolchains Print list of supported toolchains\n\
3737
run Run code generator\n\
38+
rpc Run remote procedure call server\n\
3839
update-rte Create/update configuration files and validate solution\n\n\
3940
Options:\n\
4041
-c, --context arg [...] Input context names [<project-name>][.<build-type>][+<target-type>]\n\
@@ -63,6 +64,7 @@ ProjMgr::ProjMgr() :
6364
m_extGenerator(&m_parser),
6465
m_worker(&m_parser, &m_extGenerator),
6566
m_emitter(&m_parser, &m_worker),
67+
m_rpcServer(this),
6668
m_checkSchema(false),
6769
m_missingPacks(false),
6870
m_updateRteFiles(true),
@@ -162,6 +164,7 @@ int ProjMgr::ParseCommandLine(int argc, char** argv) {
162164
cxxopts::Option updateIdx("update-idx", "Update cbuild-idx file with layer info", cxxopts::value<bool>()->default_value("false"));
163165
cxxopts::Option quiet("q,quiet", "Run silently, printing only error messages", cxxopts::value<bool>()->default_value("false"));
164166
cxxopts::Option cbuildgen("cbuildgen", "Generate legacy *.cprj files", cxxopts::value<bool>()->default_value("false"));
167+
cxxopts::Option contentLength("content-length", "Prepend 'Content-Length' header to JSON RPC requests and responses", cxxopts::value<bool>()->default_value("false"));
165168

166169
// command options dictionary
167170
map<string, std::pair<bool, vector<cxxopts::Option>>> optionsDict = {
@@ -180,6 +183,7 @@ int ProjMgr::ParseCommandLine(int argc, char** argv) {
180183
{"list layers", { false, {context, contextSet, debug, load, clayerSearchPath, quiet, schemaCheck, toolchain, verbose, updateIdx}}},
181184
{"list toolchains", { false, {context, contextSet, debug, quiet, toolchain, verbose}}},
182185
{"list environment", { true, {}}},
186+
{"rpc", { true, {contentLength}}},
183187
};
184188

185189
try {
@@ -188,7 +192,7 @@ int ProjMgr::ParseCommandLine(int argc, char** argv) {
188192
solution, context, contextSet, filter, generator,
189193
load, clayerSearchPath, missing, schemaCheck, noUpdateRte, output, outputAlt,
190194
help, version, verbose, debug, dryRun, exportSuffix, toolchain, ymlOrder,
191-
relativePaths, frozenPacks, updateIdx, quiet, cbuildgen
195+
relativePaths, frozenPacks, updateIdx, quiet, cbuildgen, contentLength
192196
});
193197
options.parse_positional({ "positional" });
194198

@@ -215,6 +219,7 @@ int ProjMgr::ParseCommandLine(int argc, char** argv) {
215219
m_cbuildgen = parseResult.count("cbuildgen");
216220
m_worker.SetCbuild2Cmake(!m_cbuildgen);
217221
ProjMgrLogger::m_quiet = parseResult.count("quiet");
222+
m_rpcServer.SetContentLengthHeader(parseResult.count("content-length"));
218223

219224
vector<string> positionalArguments;
220225
if (parseResult.count("positional")) {
@@ -403,6 +408,11 @@ int ProjMgr::ProcessCommands() {
403408
if (!RunCodeGenerator()) {
404409
return ErrorCode::ERROR;
405410
}
411+
} else if (m_command == "rpc") {
412+
// Launch 'rpc' server
413+
if (!m_rpcServer.Run()) {
414+
return ErrorCode::ERROR;
415+
}
406416
} else {
407417
ProjMgrLogger::Get().Error("<command> was not found");
408418
return ErrorCode::ERROR;
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* Copyright (c) 2025 Arm Limited. All rights reserved.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include "ProjMgrRpcServer.h"
8+
#include "ProjMgr.h"
9+
#include "ProductInfo.h"
10+
#include "ProductInfo.h"
11+
12+
#include <jsonrpccxx/server.hpp>
13+
#include <iostream>
14+
15+
using namespace std;
16+
using namespace jsonrpccxx;
17+
18+
static constexpr const char* CONTENT_LENGTH_HEADER = "Content-Length:";
19+
20+
ProjMgrRpcServer::ProjMgrRpcServer(ProjMgr* manager) :
21+
m_manager(manager) {
22+
}
23+
24+
ProjMgrRpcServer::~ProjMgrRpcServer(void) {
25+
// Reserved
26+
}
27+
28+
const string ProjMgrRpcServer::GetRequestFromStdinWithLength(void) {
29+
string line;
30+
int contentLength = 0;
31+
const string& header = CONTENT_LENGTH_HEADER;
32+
while (getline(cin, line) && !line.empty() && !cin.fail()) {
33+
if (line.find(header) == 0) {
34+
contentLength = RteUtils::StringToInt(line.substr(header.length()), 0);
35+
}
36+
}
37+
string request(contentLength, '\0');
38+
cin.read(&request[0], contentLength);
39+
return request;
40+
}
41+
42+
const string ProjMgrRpcServer::GetRequestFromStdin(void) {
43+
string jsonData;
44+
int braces = 0;
45+
bool inJson = false;
46+
char c;
47+
while (cin.get(c)) {
48+
if (c == '{') {
49+
braces++;
50+
inJson = true;
51+
}
52+
if (c == '}') {
53+
braces--;
54+
}
55+
if (inJson) {
56+
jsonData += c;
57+
}
58+
if (inJson && braces == 0) {
59+
break;
60+
}
61+
}
62+
return jsonData;
63+
}
64+
65+
class RpcHandler {
66+
public:
67+
RpcHandler(ProjMgrRpcServer* server) : m_server(server) {}
68+
string GetVersion(void);
69+
bool Shutdown(void);
70+
bool LoadPacks(void);
71+
bool LoadSolution(string solution);
72+
protected:
73+
ProjMgrRpcServer* m_server;
74+
};
75+
76+
bool ProjMgrRpcServer::Run(void) {
77+
JsonRpc2Server jsonServer;
78+
RpcHandler handler(this);
79+
80+
// Register supported RPC functions
81+
jsonServer.Add("GetVersion" , GetHandle(&RpcHandler::GetVersion , handler));
82+
jsonServer.Add("Shutdown" , GetHandle(&RpcHandler::Shutdown , handler));
83+
jsonServer.Add("LoadPacks" , GetHandle(&RpcHandler::LoadPacks , handler));
84+
jsonServer.Add("LoadSolution", GetHandle(&RpcHandler::LoadSolution, handler));
85+
86+
while (!m_shutdown) {
87+
// Get request
88+
const auto request = m_contextLength ?
89+
GetRequestFromStdinWithLength() :
90+
GetRequestFromStdin();
91+
92+
// Handle request
93+
const auto response = jsonServer.HandleRequest(request);
94+
95+
// Send response
96+
if (m_contextLength) {
97+
cout << CONTENT_LENGTH_HEADER << response.size() << std::endl << std::endl;
98+
}
99+
cout << response << std::flush;
100+
}
101+
102+
return true;
103+
}
104+
105+
string RpcHandler::GetVersion(void) {
106+
return VERSION_STRING;
107+
}
108+
109+
bool RpcHandler::Shutdown(void) {
110+
m_server->SetShutdown(true);
111+
return true;
112+
}
113+
114+
bool RpcHandler::LoadPacks(void) {
115+
m_server->GetManager()->GetWorker().LoadAllRelevantPacks();
116+
return true;
117+
}
118+
119+
bool RpcHandler::LoadSolution(string solution) {
120+
//m_server->GetManager()->GetWorker()
121+
return true;
122+
}
123+

0 commit comments

Comments
 (0)