valentina/qbs/modules/vcs2/vcs2.qbs
Roman Telezhynskyi fda3ec4783 Improve reading repo information.
Relying on logs/HEAD in case of CI doesn't work.
2024-03-29 17:20:57 +02:00

202 lines
7.4 KiB
QML

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];
}
}
}