Skip to content

Commit 4818619

Browse files
authored
Merge pull request #53 from cgkantidis/main
Add the option to write the results to a JSON file
2 parents 9931b94 + 8572e25 commit 4818619

File tree

10 files changed

+140
-18
lines changed

10 files changed

+140
-18
lines changed

CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ file(GLOB SOURCES src/*.cpp)
44

55
SET(DUPLO_VERSION "\"v1.0.1\"" CACHE STRING "Duplo version")
66

7+
include(FetchContent)
8+
FetchContent_Declare(
9+
nlohmann_json
10+
GIT_REPOSITORY https://github.com/nlohmann/json.git
11+
GIT_TAG v3.11.3
12+
GIT_SHALLOW 1
13+
OVERRIDE_FIND_PACKAGE
14+
)
15+
FetchContent_MakeAvailable(nlohmann_json)
16+
717
add_executable(duplo ${SOURCES})
818

919
set_target_properties(duplo PROPERTIES
@@ -13,6 +23,8 @@ set_target_properties(duplo PROPERTIES
1323

1424
target_compile_definitions(duplo PRIVATE DUPLO_VERSION=${DUPLO_VERSION})
1525
target_include_directories(duplo PRIVATE src/include/)
26+
find_package(nlohmann_json 3.11.3 REQUIRED)
27+
target_link_libraries(duplo PRIVATE nlohmann_json::nlohmann_json)
1628

1729
if(NOT MSVC)
1830
target_compile_options(duplo PRIVATE -Wall -Werror)

src/Duplo.cpp

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@
77
#include "StringUtil.h"
88
#include "TextFile.h"
99

10+
#include <nlohmann/json.hpp>
11+
1012
#include <algorithm>
1113
#include <cmath>
1214
#include <cstring>
1315
#include <ctime>
1416
#include <fstream>
17+
#include <optional>
1518
#include <sstream>
1619
#include <tuple>
1720
#include <unordered_map>
@@ -20,6 +23,7 @@
2023
typedef std::tuple<unsigned, std::string> FileLength;
2124
typedef const std::string* StringPtr;
2225
typedef std::unordered_map<unsigned long, std::vector<StringPtr>> HashToFiles;
26+
using json = nlohmann::json;
2327

2428
class ProcessResult {
2529
unsigned m_blocks;
@@ -159,14 +163,36 @@ namespace {
159163
return std::tuple(std::move(sourceFiles), matrix, files, locsTotal);
160164
}
161165

166+
void ReportSeqJSON(
167+
int begin,
168+
int end,
169+
int src_begin1,
170+
int src_end1,
171+
int src_begin2,
172+
int src_end2,
173+
const SourceFile& source1,
174+
const SourceFile& source2,
175+
json& json_out) {
176+
json_out.emplace_back(json{
177+
{ "LineCount", end - begin },
178+
{ "SourceFile1", source1.GetFilename() },
179+
{ "StartLineNumber1", src_begin1 },
180+
{ "EndLineNumber1", src_end1 },
181+
{ "SourceFile2", source2.GetFilename() },
182+
{ "StartLineNumber2", src_begin2 },
183+
{ "EndLineNumber2", src_end2 },
184+
{ "Lines", source1.GetLines(begin, end) } });
185+
}
186+
162187
void ReportSeq(
163188
int line1,
164189
int line2,
165190
int count,
166191
bool xml,
167192
const SourceFile& source1,
168193
const SourceFile& source2,
169-
std::ostream& out) {
194+
std::ostream& out,
195+
std::optional<json>& json_out) {
170196
if (xml) {
171197
out
172198
<< " <set LineCount=\"" << count << "\">"
@@ -209,6 +235,17 @@ namespace {
209235

210236
out << " </lines>" << std::endl;
211237
out << " </set>" << std::endl;
238+
} else if (json_out) {
239+
ReportSeqJSON(
240+
line1,
241+
line1 + count,
242+
source1.GetLine(line1).GetLineNumber(),
243+
source1.GetLine(line1 + count).GetLineNumber(),
244+
source2.GetLine(line2).GetLineNumber(),
245+
source2.GetLine(line2 + count).GetLineNumber(),
246+
source1,
247+
source2,
248+
json_out.value());
212249
} else {
213250
out
214251
<< source1.GetFilename()
@@ -231,7 +268,8 @@ namespace {
231268
const SourceFile& source2,
232269
std::vector<bool>& matrix,
233270
const Options& options,
234-
std::ostream& outFile) {
271+
std::ostream& outFile,
272+
std::optional<json>& json_out) {
235273
size_t m = source1.GetNumOfLines();
236274
size_t n = source2.GetNumOfLines();
237275

@@ -279,7 +317,8 @@ namespace {
279317
options.GetOutputXml(),
280318
source1,
281319
source2,
282-
outFile);
320+
outFile,
321+
json_out);
283322
duplicateLines += seqLen;
284323
blocks++;
285324
}
@@ -300,7 +339,8 @@ namespace {
300339
options.GetOutputXml(),
301340
source1,
302341
source2,
303-
outFile);
342+
outFile,
343+
json_out);
304344
duplicateLines += seqLen;
305345
blocks++;
306346
}
@@ -324,7 +364,8 @@ namespace {
324364
options.GetOutputXml(),
325365
source1,
326366
source2,
327-
outFile);
367+
outFile,
368+
json_out);
328369
duplicateLines += seqLen;
329370
blocks++;
330371
}
@@ -340,7 +381,8 @@ namespace {
340381
options.GetOutputXml(),
341382
source1,
342383
source2,
343-
outFile);
384+
outFile,
385+
json_out);
344386
duplicateLines += seqLen;
345387
blocks++;
346388
}
@@ -357,7 +399,7 @@ int Duplo::Run(const Options& options) {
357399
std::ofstream of;
358400
if (options.GetOutputFilename() == "-") {
359401
buf = std::cout.rdbuf();
360-
if (options.GetOutputXml() == false) {
402+
if (!options.GetOutputXml() && !options.GetOutputJSON()) {
361403
logbuf = std::cout.rdbuf();
362404
}
363405
else {
@@ -391,6 +433,8 @@ int Duplo::Run(const Options& options) {
391433
<< std::endl;
392434
}
393435

436+
auto json_out = options.GetOutputJSON() ? std::optional(json()) : std::nullopt;
437+
394438
auto lines = LoadFileList(options.GetListFilename());
395439
auto [sourceFiles, matrix, files, locsTotal] = LoadSourceFiles(
396440
lines,
@@ -429,7 +473,8 @@ int Duplo::Run(const Options& options) {
429473
left,
430474
matrix,
431475
options,
432-
out);
476+
out,
477+
json_out);
433478

434479
// files to compare are those that have matching lines
435480
for (unsigned j = i + 1; j < sourceFiles.size(); j++) {
@@ -442,11 +487,12 @@ int Duplo::Run(const Options& options) {
442487
right,
443488
matrix,
444489
options,
445-
out);
490+
out,
491+
json_out);
446492
}
447493
}
448494

449-
if (options.GetOutputXml() == false) {
495+
if (!options.GetOutputXml() && !options.GetOutputJSON()) {
450496
if (processResult.Blocks() > 0) {
451497
log
452498
<< left.GetFilename()
@@ -465,6 +511,8 @@ int Duplo::Run(const Options& options) {
465511
out
466512
<< "</duplo>"
467513
<< std::endl;
514+
} else if (json_out) {
515+
out << json_out->dump(2);
468516
} else {
469517
out
470518
<< "Configuration:"

src/Main.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ namespace {
2525
auto minChars = ap.getInt("-mc", MIN_CHARS);
2626
bool ignorePrepStuff = ap.is("-ip");
2727
bool outputXml = ap.is("-xml");
28+
bool outputJSON = ap.is("-json");
2829
bool ignoreSameFilename = ap.is("-d");
2930
std::string listFilename(argv[argc - 2]);
3031
std::string outputFilename(argv[argc - 1]);
@@ -35,6 +36,7 @@ namespace {
3536
blockPercentThreshold,
3637
numberOfFiles,
3738
outputXml,
39+
outputJSON,
3840
ignoreSameFilename,
3941
listFilename,
4042
outputFilename);
@@ -60,6 +62,7 @@ namespace {
6062
std::cout << " -ip ignore preprocessor directives\n";
6163
std::cout << " -d ignore file pairs with same name\n";
6264
std::cout << " -xml output file in XML\n";
65+
std::cout << " -json output file in JSON format\n";
6366
std::cout << " INPUT_FILELIST input filelist (specify '-' to read from stdin)\n";
6467
std::cout << " OUTPUT_FILE output file (specify '-' to output to stdout)\n";
6568

src/Options.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Options::Options(
77
unsigned blockPercentThreshold,
88
unsigned filesToCheck,
99
bool outputXml,
10+
bool outputJSON,
1011
bool ignoreSameFilename,
1112
const std::string& listFilename,
1213
const std::string& outputFilename)
@@ -16,6 +17,7 @@ Options::Options(
1617
, m_blockPercentThreshold(blockPercentThreshold)
1718
, m_filesToCheck(filesToCheck)
1819
, m_outputXml(outputXml)
20+
, m_outputJSON(outputJSON)
1921
, m_ignoreSameFilename(ignoreSameFilename)
2022
, m_listFilename(listFilename)
2123
, m_outputFilename(outputFilename)
@@ -42,6 +44,10 @@ bool Options::GetOutputXml() const {
4244
return m_outputXml;
4345
}
4446

47+
bool Options::GetOutputJSON() const {
48+
return m_outputJSON;
49+
}
50+
4551
bool Options::GetIgnoreSameFilename() const {
4652
return m_ignoreSameFilename;
4753
}
@@ -56,4 +62,4 @@ const std::string& Options::GetOutputFilename() const {
5662

5763
size_t Options::GetFilesToCheck() const {
5864
return m_filesToCheck;
59-
}
65+
}

src/SourceFile.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
#include <algorithm>
99
#include <cassert>
10+
#include <iterator>
11+
#include <vector>
1012

1113
SourceFile::SourceFile(const std::string& filename, unsigned minChars, bool ignorePrepStuff)
1214
: m_filename(filename),
@@ -31,6 +33,19 @@ const SourceLine& SourceFile::GetLine(int index) const {
3133
return m_sourceLines[index];
3234
}
3335

36+
std::vector<std::string> SourceFile::GetLines(int begin, int end) const {
37+
std::vector<std::string> lines;
38+
lines.reserve(end - begin);
39+
auto begin_it = std::next(m_sourceLines.begin(), begin);
40+
auto end_it = std::next(m_sourceLines.begin(), end);
41+
std::transform(begin_it, end_it, std::back_inserter(lines),
42+
[](SourceLine const& line) {
43+
return line.GetLine();
44+
}
45+
);
46+
return lines;
47+
}
48+
3449
const std::string& SourceFile::GetFilename() const {
3550
return m_filename;
3651
}

src/include/Options.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class Options {
1010
unsigned m_blockPercentThreshold;
1111
unsigned m_filesToCheck;
1212
bool m_outputXml;
13+
bool m_outputJSON;
1314
bool m_ignoreSameFilename;
1415
std::string m_listFilename;
1516
std::string m_outputFilename;
@@ -22,6 +23,7 @@ class Options {
2223
unsigned blockPercentThreshold,
2324
unsigned m_filesToCheck,
2425
bool outputXml,
26+
bool outputJSON,
2527
bool ignoreSameFilename,
2628
const std::string& listFilename,
2729
const std::string& outputFilename
@@ -31,6 +33,7 @@ class Options {
3133
const std::string& GetListFilename() const;
3234
const std::string& GetOutputFilename() const;
3335
bool GetOutputXml() const;
36+
bool GetOutputJSON() const;
3437
unsigned GetMinChars() const;
3538
bool GetIgnorePrepStuff() const;
3639
unsigned GetMinBlockSize() const;

src/include/SourceFile.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class SourceFile {
1717

1818
size_t GetNumOfLines() const;
1919
const SourceLine& GetLine(int index) const;
20+
std::vector<std::string> GetLines(int begin, int end) const;
2021
const std::string& GetFilename() const;
2122

2223
bool operator==(const SourceFile& other) const;

tests/Simple/expected-json.log

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[
2+
{
3+
"EndLineNumber1": 13,
4+
"EndLineNumber2": 7,
5+
"LineCount": 5,
6+
"Lines": [
7+
"AAAAA",
8+
"BBBBB",
9+
"CCCCC",
10+
"DDDDD",
11+
"EEEEE"
12+
],
13+
"SourceFile1": "tests/Simple/LineNumbers.c",
14+
"SourceFile2": "tests/Simple/LineNumbers.c",
15+
"StartLineNumber1": 7,
16+
"StartLineNumber2": 1
17+
}
18+
]

tests/Simple/test-json.bats

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/bin/bash
2+
3+
@test "LineNumbers.c" {
4+
run ./build/duplo -json tests/Simple/LineNumbers.lst out.json
5+
[ "$status" -eq 1 ]
6+
[ "${lines[0]}" = "Loading and hashing files ... 2 done." ]
7+
}
8+
9+
@test "LineNumbers.c out.json" {
10+
run diff <(cat tests/Simple/expected-json.log) <(./build/duplo -json tests/Simple/LineNumbers.lst -)
11+
[ "$status" -eq 0 ]
12+
printf 'Lines:\n'
13+
printf 'lines %s\n' "${lines[@]}" >&2
14+
printf 'output %s\n' "${output[@]}" >&2
15+
}

tests/test.bats

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,12 @@
2121
[ "${lines[14]}" = " -ip ignore preprocessor directives" ]
2222
[ "${lines[15]}" = " -d ignore file pairs with same name" ]
2323
[ "${lines[16]}" = " -xml output file in XML" ]
24-
[ "${lines[17]}" = " INPUT_FILELIST input filelist (specify '-' to read from stdin)" ]
25-
[ "${lines[18]}" = " OUTPUT_FILE output file (specify '-' to output to stdout)" ]
26-
[ "${lines[19]}" = "VERSION" ]
27-
[ "${lines[21]}" = "AUTHORS" ]
28-
[ "${lines[22]}" = " Daniel Lidstrom ([email protected])" ]
29-
[ "${lines[23]}" = " Christian M. Ammann ([email protected])" ]
30-
[ "${lines[24]}" = " Trevor D'Arcy-Evans ([email protected])" ]
24+
[ "${lines[17]}" = " -json output file in JSON format" ]
25+
[ "${lines[18]}" = " INPUT_FILELIST input filelist (specify '-' to read from stdin)" ]
26+
[ "${lines[19]}" = " OUTPUT_FILE output file (specify '-' to output to stdout)" ]
27+
[ "${lines[20]}" = "VERSION" ]
28+
[ "${lines[22]}" = "AUTHORS" ]
29+
[ "${lines[23]}" = " Daniel Lidstrom ([email protected])" ]
30+
[ "${lines[24]}" = " Christian M. Ammann ([email protected])" ]
31+
[ "${lines[25]}" = " Trevor D'Arcy-Evans ([email protected])" ]
3132
}

0 commit comments

Comments
 (0)