diff --git a/dub.json b/dub.json index 9a55555..f0713ef 100644 --- a/dub.json +++ b/dub.json @@ -1,6 +1,6 @@ { "name": "ldcup", - "version": "1.1.7", + "version": "1.1.8", "description": "Download and manage D compiler [ldc2].", "license": "Apache-2.0", "targetPath": "bin", diff --git a/source/app.d b/source/app.d index 5f3db56..06d92bd 100644 --- a/source/app.d +++ b/source/app.d @@ -21,6 +21,7 @@ struct Args bool help; bool verbose; bool remote; + bool compilerSet; string installDir; string platform; string command; @@ -36,7 +37,7 @@ void printHelp(string programName) @safe writeln(" install [compiler] Install a compiler (default: ldc2-latest)"); writeln(" uninstall Uninstall an installed compiler"); writeln(" list List installed compilers"); - writeln(" run -- Run ldc2 with the given flags"); + writeln(" run [compiler] -- Run ldc2 with the given flags"); writeln(); writeln("Compiler specifiers:"); writeln(" ldc2-latest Latest stable LDC2 release (default)"); @@ -88,9 +89,22 @@ Args parseArgs(string[] argv) @safe break; } else if (arg.startsWith("ldc2-") || arg.startsWith("opend-")) + { parsed.compiler = normaliseCompilerSpec(arg); - else if (arg.canFind("redub")) + parsed.compilerSet = true; + } + else if (arg == "redub") + { parsed.compiler = arg; + parsed.compilerSet = true; + } + else if (arg.among("dmd", "gdc")) + throw new Exception("Only ldc2 and opend compilers are supported."); + else if (arg.startsWith("v") && arg.length > 1 && arg[1].isDigit) + { + parsed.compiler = normaliseCompilerSpec(arg); + parsed.compilerSet = true; + } else if (parsed.command.empty) parsed.command = arg; else @@ -118,12 +132,6 @@ int main(string[] argv) @safe return 0; } - if (parsed.compiler.among("dmd", "gdc")) - { - writeln("Error: only ldc2 and opend compilers are supported."); - return 1; - } - if (parsed.command.empty) { writeln("Error: no command specified."); @@ -143,6 +151,7 @@ int main(string[] argv) @safe break; case "uninstall": + enforce(parsed.compilerSet, "uninstall requires a compiler specifier, e.g. ldc2-1.42.0"); manager.uninstallCompiler(parsed.compiler); break; diff --git a/source/impl.d b/source/impl.d index 2297095..321b9b7 100644 --- a/source/impl.d +++ b/source/impl.d @@ -63,10 +63,10 @@ private: Arch currentArch; ReleaseType releaseType; - version (Windows) - immutable string ext = ".7z"; - else - immutable string ext = ".tar.xz"; + private string ext() const @safe pure + { + return (currentOS == OS.windows) ? ".7z" : ".tar.xz"; + } public: bool verbose; @@ -137,6 +137,9 @@ public: enforce(parts.length == 2, "Invalid platform format (expected OS-ARCH): " ~ platform); currentOS = fromString!OS(parts[0]); currentArch = fromString!Arch(parts[1]); + // Windows releases use x64/x86/multilib naming, not x86_64 + if (currentOS == OS.windows && currentArch == Arch.x86_64) + currentArch = Arch.x64; } } @@ -150,9 +153,9 @@ public: auto downloadUrl = getCompilerDownloadUrl(resolvedCompiler); auto targetPath = buildPath(root, resolvedCompiler); - downloadAndExtract(downloadUrl, targetPath, resolvedCompiler.startsWith("opend-")); + bool isOpend = resolvedCompiler.startsWith("opend-"); + downloadAndExtract(downloadUrl, targetPath, isOpend); - bool isOpend = compilerSpec.startsWith("opend-"); string prefix = isOpend ? "opend" : "ldc2"; compilerPath = buildPath( targetPath, @@ -198,10 +201,9 @@ public: void runCompiler(string compilerSpec, string[] args) @safe { - enforce(args.length > 0, "No flags provided. Use 'run -- '"); + enforce(args.length > 0, "No flags provided. Use 'run [compiler] -- '"); log("Running compiler: %s", compilerSpec); - compilerPath = findLDC2Path(); - enforce(!compilerPath.empty, "No LDC2 installation found"); + compilerPath = findLDC2Path(compilerSpec); auto cmd = [compilerPath] ~ args; auto result = execute(cmd); @@ -209,14 +211,18 @@ public: enforce(result.status == 0, "LDC2 execution failed with status %d".format(result.status)); } - string findLDC2Path() @safe + string findLDC2Path(string compilerSpec = "") @safe { auto installed = listInstalledCompilers().filter!(v => v.startsWith("ldc2-")).array; enforce(!installed.empty, "No LDC2 installation found in %s".format(root)); - string ver = installed[0]["ldc2-".length .. $]; + // Use the requested spec if it's installed; otherwise fall back to the first entry. + string entry = (compilerSpec.startsWith("ldc2-") && installed.canFind(compilerSpec)) + ? compilerSpec : installed[0]; + + string ver = entry["ldc2-".length .. $]; string ldc2Dir = buildPath( - root, installed[0], + root, entry, format("ldc2-%s-%s-%s", ver, currentOS, currentArch), "bin" ); @@ -467,7 +473,7 @@ public: else if (compilerSpec.endsWith("nightly") || compilerSpec.endsWith("master")) { releaseType = ReleaseType.nightly; - url = "https://github.com/ldc-developers/ldc/commits/master.atom"; + url = "https://api.github.com/repos/ldc-developers/ldc/releases/tags/CI"; } else // Explicit version — use as-is; normalise prefix if missing. return compilerSpec.startsWith("ldc2-") ? compilerSpec : "ldc2-" ~ compilerSpec; @@ -485,9 +491,24 @@ public: format("HTTP %d when resolving %s version", res.code, releaseType)); string response = (cast(string) res.responseBody.data).strip; - string dversion = (releaseType == ReleaseType.nightly) - ? response.split("tag:github.com,2008:Grit::Commit/")[1].split( - "")[0][0 .. 8] : response; + string dversion; + if (releaseType == ReleaseType.nightly) + { + string suffix = format("-%s-%s%s", currentOS, currentArch, ext); + foreach (asset; parseJSON(response)["assets"].array) + { + string name = asset["name"].str; + if (name.startsWith("ldc2-") && name.endsWith(suffix)) + { + dversion = name["ldc2-".length .. $ - suffix.length]; + break; + } + } + enforce(!dversion.empty, + "No CI nightly build found for %s-%s".format(currentOS, currentArch)); + } + else + dversion = response; log("Resolved %s version: ldc2-%s", releaseType, dversion); return "ldc2-" ~ dversion;