1 /**
2 This tool coverts D code coverage files from absolute to relative paths.
3 
4 D code coverage files are written using absolute path names if absolute paths are
5 used in the build command. (This enables running coverage tests from a directory
6 other than the original build directory.) This tool converts the files to relative
7 paths. This includes the file name and the name included in the file.
8 
9 Copyright (c) 2017, eBay Software Foundation
10 Initially written by Jon Degenhardt
11 
12 License: Boost Licence 1.0 (http://boost.org/LICENSE_1_0.txt)
13 
14 **/
15 module codecov_to_relative_path;
16 
17 import std.algorithm : findSplit;
18 import std.array : appender;
19 import std.conv : to;
20 import std.file : exists, isDir, isFile, remove, rename;
21 import std.path : absolutePath, baseName, buildPath, buildNormalizedPath, dirName, extension,
22     isAbsolute, stripExtension;
23 import std.range : empty;
24 import std.stdio;
25 import std..string : tr;
26 
27 int main(string[] cmdArgs)
28 {
29     auto programName = (cmdArgs.length > 0) ? cmdArgs[0].stripExtension.baseName : "Unknown_program_name";
30 
31     if (cmdArgs.length < 2)
32     {
33         writefln("Synopsis: %s coverage-file [coverage-file...]", programName);
34         return 1;
35     }
36 
37     auto coverageFiles = cmdArgs[1..$];
38 
39     foreach (cf; coverageFiles)
40     {
41         if (!cf.exists || !cf.isFile)
42         {
43             writefln("%s is not a file", cf);
44             return 1;
45         }
46     }
47     
48     foreach (cf; coverageFiles)
49     {
50         auto rootDir = cf.absolutePath.buildNormalizedPath.dirName;
51         auto fileName = cf.baseName;
52         auto fileNameNoExt = fileName.stripExtension;
53         auto lines = appender!(string[])();
54         foreach (l; cf.File.byLine) lines ~= l.to!string;
55         if (lines.data.length > 0)
56         {
57             /* Check that the last line matches our file name. */
58             auto lastLine = lines.data[$ - 1];
59             auto lastLineSplit = lastLine.findSplit(" ");
60             auto lastLinePath = lastLineSplit[1].empty ? "" : lastLineSplit[0];
61             auto lastLinePathNoExt = lastLinePath.stripExtension;
62             if (lastLinePath.isAbsolute && 
63                 lastLinePathNoExt.tr("\\/", "--") == fileNameNoExt &&
64                 rootDir.length + 1 <= lastLine.length &&
65                 rootDir.length + 1 <= fileName.length)
66             {
67                 auto updatedLastLine = lastLine[rootDir.length + 1 .. $];
68                 auto newFileName = fileName[rootDir.length + 1 .. $];
69                 if (newFileName != fileName)
70                 {
71                     auto ofile = newFileName.File("w");
72                     foreach (l; lines.data[0 .. $ - 1]) ofile.writeln(l);
73                     ofile.writeln(updatedLastLine);
74                     fileName.remove;
75                 }
76             }
77         }
78     }
79 
80     return 0;
81 }