diff --git a/build-files.txt b/build-files.txt index fbda121..34171d7 100644 --- a/build-files.txt +++ b/build-files.txt @@ -29,6 +29,7 @@ source/dub/generators/sublimetext.d source/dub/generators/targetdescription.d source/dub/generators/visuald.d +source/dub/internal/git.d source/dub/internal/libInputVisitor.d source/dub/internal/sdlang/ast.d source/dub/internal/sdlang/exception.d diff --git a/source/dub/internal/git.d b/source/dub/internal/git.d new file mode 100644 index 0000000..50be963 --- /dev/null +++ b/source/dub/internal/git.d @@ -0,0 +1,103 @@ +module dub.internal.git; + +import dub.internal.vibecompat.core.file; +import dub.internal.vibecompat.core.log; +import std.file; +import std.string; + +version (Windows) +{ + import dub.internal.vibecompat.data.json; + + string determineVersionWithGit(NativePath path) + { + // On Windows, which is slow at running external processes, + // cache the version numbers that are determined using + // git to speed up the initialization phase. + import dub.internal.utils : jsonFromFile; + + // quickly determine head commit without invoking git + string head_commit; + auto hpath = (path ~ ".git/HEAD").toNativeString(); + if (exists(hpath)) { + auto head_ref = readText(hpath).strip(); + if (head_ref.startsWith("ref: ")) { + auto rpath = (path ~ (".git/"~head_ref[5 .. $])).toNativeString(); + if (exists(rpath)) + head_commit = readText(rpath).strip(); + } + } + + // return the last determined version for that commit + // not that this is not always correct, most notably when + // a tag gets added/removed/changed and changes the outcome + // of the full version detection computation + auto vcachepath = path ~ ".dub/version.json"; + if (existsFile(vcachepath)) { + auto ver = jsonFromFile(vcachepath); + if (head_commit == ver["commit"].opt!string) + return ver["version"].get!string; + } + + // if no cache file or the HEAD commit changed, perform full detection + auto ret = determineVersionWithGitTool(path); + + // update version cache file + if (head_commit.length) { + import dub.internal.utils : atomicWriteJsonFile; + + if (!existsFile(path ~".dub")) createDirectory(path ~ ".dub"); + atomicWriteJsonFile(vcachepath, Json(["commit": Json(head_commit), "version": Json(ret)])); + } + + return ret; + } +} +else +{ + string determineVersionWithGit(NativePath path) + { + return determineVersionWithGitTool(path); + } +} + +// determines the version of a package that is stored in a Git working copy +// by invoking the "git" executable +private string determineVersionWithGitTool(NativePath path) +{ + import dub.semver; + import std.algorithm : canFind; + import std.conv : to; + import std.process; + + auto git_dir = path ~ ".git"; + if (!existsFile(git_dir) || !isDir(git_dir.toNativeString)) return null; + auto git_dir_param = "--git-dir=" ~ git_dir.toNativeString(); + + static string exec(scope string[] params...) { + auto ret = executeShell(escapeShellCommand(params)); + if (ret.status == 0) return ret.output.strip; + logDebug("'%s' failed with exit code %s: %s", params.join(" "), ret.status, ret.output.strip); + return null; + } + + auto tag = exec("git", git_dir_param, "describe", "--long", "--tags"); + if (tag !is null) { + auto parts = tag.split("-"); + auto commit = parts[$-1]; + auto num = parts[$-2].to!int; + tag = parts[0 .. $-2].join("-"); + if (tag.startsWith("v") && isValidVersion(tag[1 .. $])) { + if (num == 0) return tag[1 .. $]; + else if (tag.canFind("+")) return format("%s.commit.%s.%s", tag[1 .. $], num, commit); + else return format("%s+commit.%s.%s", tag[1 .. $], num, commit); + } + } + + auto branch = exec("git", git_dir_param, "rev-parse", "--abbrev-ref", "HEAD"); + if (branch !is null) { + if (branch != "HEAD") return "~" ~ branch; + } + + return null; +} diff --git a/source/dub/package_.d b/source/dub/package_.d index 88e3765..c7c7750 100644 --- a/source/dub/package_.d +++ b/source/dub/package_.d @@ -744,85 +744,11 @@ private string determineVersionFromSCM(NativePath path) { - // On Windows, which is slow at running external processes, - // cache the version numbers that are determined using - // GIT to speed up the initialization phase. - version (Windows) { - import std.file : exists, readText; + if (existsFile(path ~ ".git")) + { + import dub.internal.git : determineVersionWithGit; - // quickly determine head commit without invoking GIT - string head_commit; - auto hpath = (path ~ ".git/HEAD").toNativeString(); - if (exists(hpath)) { - auto head_ref = readText(hpath).strip(); - if (head_ref.startsWith("ref: ")) { - auto rpath = (path ~ (".git/"~head_ref[5 .. $])).toNativeString(); - if (exists(rpath)) - head_commit = readText(rpath).strip(); - } - } - - // return the last determined version for that commit - // not that this is not always correct, most notably when - // a tag gets added/removed/changed and changes the outcome - // of the full version detection computation - auto vcachepath = path ~ ".dub/version.json"; - if (existsFile(vcachepath)) { - auto ver = jsonFromFile(vcachepath); - if (head_commit == ver["commit"].opt!string) - return ver["version"].get!string; - } + return determineVersionWithGit(path); } - - // if no cache file or the HEAD commit changed, perform full detection - auto ret = determineVersionWithGIT(path); - - version (Windows) { - // update version cache file - if (head_commit.length) { - if (!existsFile(path ~".dub")) createDirectory(path ~ ".dub"); - atomicWriteJsonFile(vcachepath, Json(["commit": Json(head_commit), "version": Json(ret)])); - } - } - - return ret; -} - -// determines the version of a package that is stored in a GIT working copy -// by invoking the "git" executable -private string determineVersionWithGIT(NativePath path) -{ - import std.process; - import dub.semver; - - auto git_dir = path ~ ".git"; - if (!existsFile(git_dir) || !isDir(git_dir.toNativeString)) return null; - auto git_dir_param = "--git-dir=" ~ git_dir.toNativeString(); - - static string exec(scope string[] params...) { - auto ret = executeShell(escapeShellCommand(params)); - if (ret.status == 0) return ret.output.strip; - logDebug("'%s' failed with exit code %s: %s", params.join(" "), ret.status, ret.output.strip); - return null; - } - - auto tag = exec("git", git_dir_param, "describe", "--long", "--tags"); - if (tag !is null) { - auto parts = tag.split("-"); - auto commit = parts[$-1]; - auto num = parts[$-2].to!int; - tag = parts[0 .. $-2].join("-"); - if (tag.startsWith("v") && isValidVersion(tag[1 .. $])) { - if (num == 0) return tag[1 .. $]; - else if (tag.canFind("+")) return format("%s.commit.%s.%s", tag[1 .. $], num, commit); - else return format("%s+commit.%s.%s", tag[1 .. $], num, commit); - } - } - - auto branch = exec("git", git_dir_param, "rev-parse", "--abbrev-ref", "HEAD"); - if (branch !is null) { - if (branch != "HEAD") return "~" ~ branch; - } - return null; }