import qbs.File import qbs.FileInfo import qbs.Process import qbs.TextFile import qbs.Utilities Module { property string type: typeProbe.type property string repoDir: project.sourceDirectory // TODO: If minimal qbs version is 1.23 replace with FileInfo.executableSuffix() readonly property string executableSuffix: project.qbs.targetOS.contains("windows") ? ".exe" : "" property string toolFilePath: { if (type === "git") return "git" + executableSuffix; if (type === "svn") return "svn" + executableSuffix; } property string headerFileName: "vcs-repo-state.h" readonly property string repoState: gitProbe.repoState || subversionProbe.repoState readonly property string repoStateTag: gitProbe.repoStateTag || subversionProbe.repoStateTag readonly property string repoStateDistance: gitProbe.repoStateDistance || subversionProbe.repoStateDistance readonly property string repoStateRevision: gitProbe.repoStateRevision || subversionProbe.repoStateRevision // Internal readonly property string includeDir: FileInfo.joinPaths(product.buildDirectory, "vcs-include") readonly property string metaDataBaseDir: typeProbe.metaDataBaseDir PropertyOptions { name: "type" allowedValues: ["git", "svn"] description: "the version control system your project is using" } Depends { name: "cpp"; condition: headerFileName } Properties { condition: headerFileName cpp.includePaths: [includeDir] } Probe { id: typeProbe property string tool: toolFilePath property string theRepoDir: repoDir property string type property string metaDataBaseDir configure: { var detector = new Process(); try { detector.setWorkingDirectory(theRepoDir); if (detector.exec(tool || "git", ["rev-parse", "--git-dir"]) === 0) { found = true; type = "git"; metaDataBaseDir = detector.readStdOut().trim(); if (!FileInfo.isAbsolutePath(metaDataBaseDir)) metaDataBaseDir = FileInfo.joinPaths(theRepoDir, metaDataBaseDir); return; } if (detector.exec(tool || "svn", ["info", "--show-item", "wc-root", "--no-newline"]) === 0) { found = true type = "svn"; metaDataBaseDir = FileInfo.joinPaths(detector.readStdOut(), ".svn"); return; } else if (detector.exec(tool || "svn", ["info"]) === 0) { if (detector.exec(tool || "svn", ["--version", "--quiet"]) === 0 && Utilities.versionCompare(detector.readStdOut().trim(), "1.9") < 0) { throw "svn too old, version >= 1.9 required"; } } } finally { detector.close(); } } } Probe { id: gitProbe condition: type === "git" property string tool: toolFilePath property string theRepoDir: repoDir property string repoState property string repoStateTag property string repoStateDistance property string repoStateRevision configure: { var commitsProbe = new Process(); try { commitsProbe.setWorkingDirectory(theRepoDir); var exitCode = commitsProbe.exec(tool, ["rev-list", "HEAD", "--count"], false); if (exitCode !== 0) { console.info("Cannot read repo state."); return; } var count = parseInt(commitsProbe.readStdOut().trim()); if (count < 1) { console.info("The repo has no commits yet."); return; } } finally { commitsProbe.close(); } var proc = new Process(); try { proc.setWorkingDirectory(theRepoDir); // tag is formatted as TAG-N-gSHA: // 1. latest stable version is TAG, or vX.Y.Z // 2. number of commits since latest stable version is N // 3. latest commit is gSHA proc.exec(tool, ["describe", "--always", "HEAD"], true); repoState = proc.readStdOut().trim(); if (repoState) { found = true; const tagSections = repoState.split("-"); if (tagSections.length >= 3) { repoStateTag = tagSections[0]; repoStateDistance = tagSections[1]; repoStateRevision = tagSections[2]; } else { repoStateRevision = tagSections[0]; } } } finally { proc.close(); } } } Probe { id: subversionProbe condition: type === "svn" property string tool: toolFilePath property string theRepoDir: repoDir property string filePath: FileInfo.joinPaths(metaDataBaseDir, "wc.db") property var timestamp: File.lastModified(filePath) property string repoState property string repoStateTag property string repoStateDistance property string repoStateRevision configure: { var proc = new Process(); try { proc.setWorkingDirectory(theRepoDir); proc.exec(tool, ["info", "-r", "HEAD", "--show-item", "revision", "--no-newline"], true); repoState = proc.readStdOut().trim(); if (repoState) found = true; } finally { proc.close(); } } } Rule { condition: headerFileName multiplex: true Artifact { filePath: FileInfo.joinPaths(product.vcs2.includeDir, product.vcs2.headerFileName) fileTags: ["hpp"] } prepare: { var cmd = new JavaScriptCommand(); cmd.description = "generating " + output.fileName; cmd.highlight = "codegen"; cmd.repoState = product.vcs2.repoState; cmd.repoStateTag = product.vcs2.repoStateTag; cmd.repoStateDistance = product.vcs2.repoStateDistance; cmd.repoStateRevision = product.vcs2.repoStateRevision; cmd.sourceCode = function() { var f = new TextFile(output.filePath, TextFile.WriteOnly); try { f.writeLine("#ifndef VCS_REPO_STATE_H"); f.writeLine("#define VCS_REPO_STATE_H"); f.writeLine('#define VCS_REPO_STATE "' + (repoState ? repoState : "none") + '"') f.writeLine('#define VCS_REPO_STATE_TAG "' + (repoStateTag ? repoStateTag : "none") + '"') f.writeLine('#define VCS_REPO_STATE_DISTANCE "' + (repoStateDistance ? repoStateDistance : "none") + '"') f.writeLine('#define VCS_REPO_STATE_REVISION "' + (repoStateRevision ? repoStateRevision : "none") + '"') f.writeLine("#endif"); } finally { f.close(); } }; return [cmd]; } } }