1 /** 2 This tool converts D code coverage files from absolute to relative paths. 3 4 D code coverage files are generated based on absolute path names if absolute paths are 5 used in the build command. This is reflected in the file's actual name, which reflects all 6 the path components. The absolute path is also listed at the end of the code coverage 7 report. 8 9 This tool checks a coverage file to see if absolute names where used. If so, it renames 10 the file and updates the report to use a relative path. 11 12 Copyright (c) 2017-2020, eBay Inc. 13 Initially written by Jon Degenhardt 14 15 License: Boost Licence 1.0 (http://boost.org/LICENSE_1_0.txt) 16 17 **/ 18 module buildtools.codecov_to_relative_paths; 19 20 import std.algorithm : findSplit; 21 import std.array : appender; 22 import std.conv : to; 23 import std.file : exists, isDir, isFile, remove, rename; 24 import std.path : absolutePath, baseName, buildPath, buildNormalizedPath, dirName, extension, 25 isAbsolute, stripExtension; 26 import std.range : empty; 27 import std.stdio; 28 import std.string : tr; 29 30 /** Convert a D code coverage file to use relative paths. 31 * 32 * Files provides on the command line are checked to see if the name represents an 33 * absolute path. If so, the file is renamed to reflect the relative name and the 34 * last line of the coverage report is changed to reflect this as well. 35 */ 36 int main(string[] cmdArgs) 37 { 38 auto programName = (cmdArgs.length > 0) ? cmdArgs[0].stripExtension.baseName : "Unknown_program_name"; 39 40 if (cmdArgs.length < 2) 41 { 42 writefln("Synopsis: %s coverage-file [coverage-file...]", programName); 43 return 1; 44 } 45 46 auto coverageFiles = cmdArgs[1..$]; 47 48 foreach (cf; coverageFiles) 49 { 50 if (!cf.exists || !cf.isFile) 51 { 52 writefln("%s is not a file", cf); 53 return 1; 54 } 55 } 56 57 foreach (cf; coverageFiles) 58 { 59 auto rootDir = cf.absolutePath.buildNormalizedPath.dirName; 60 auto fileName = cf.baseName; 61 auto fileNameNoExt = fileName.stripExtension; 62 auto lines = appender!(string[])(); 63 foreach (l; cf.File.byLine) lines ~= l.to!string; 64 if (lines.data.length > 0) 65 { 66 /* Check that the last line matches our file name. */ 67 auto lastLine = lines.data[$ - 1]; 68 auto lastLineSplit = lastLine.findSplit(" "); 69 auto lastLinePath = lastLineSplit[1].empty ? "" : lastLineSplit[0]; 70 auto lastLinePathNoExt = lastLinePath.stripExtension; 71 if (lastLinePath.isAbsolute && 72 lastLinePathNoExt.tr("\\/", "--") == fileNameNoExt && 73 rootDir.length + 1 <= lastLine.length && 74 rootDir.length + 1 <= fileName.length) 75 { 76 auto updatedLastLine = lastLine[rootDir.length + 1 .. $]; 77 auto newFileName = fileName[rootDir.length + 1 .. $]; 78 if (newFileName != fileName) 79 { 80 auto ofile = newFileName.File("w"); 81 foreach (l; lines.data[0 .. $ - 1]) ofile.writeln(l); 82 ofile.writeln(updatedLastLine); 83 fileName.remove; 84 } 85 } 86 } 87 } 88 89 return 0; 90 }