www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.announce - dubmore: Support of local and remote archive dependencies

Hi,

I want to share with you a little application I wrote. With the 
following application you can add local and remote archive 
dependencies to your dub configuration.

/+ dub.json:
{
	"name":"test"
	"dependencies":{
		"sample1":{"path":"C:\\D\\projects\\test2\\sample1.zip",
		"sample2":{"url":"http://localhost:8080/sample2.zip"
	},
}
+/

void main() {}

Place the dubmore executable into the same folder as dub and use 
dubmore instead of dub.

Remarks:
- Only json is supported
- You need to have the console application unzip in your system 
path

--
module dubmore;
import std.file, std.path, std.exception, std.process, std.array, 
std.json, std.net.curl, std.experimental.logger, std.algorithm, 
std.getopt, std.string;

void main(string[] args)
{
	globalLogLevel = LogLevel.all;
	string dubFile;
	scope(exit) if (exists(dubFile~".old")) rename(dubFile~".old", 
dubFile);
	string[] dubArgs;
	
	string packagesFolder = buildPath(getcwd(), "packages");
	if (args.canFind("--force") && exists(packagesFolder)) 
rmdirRecurse(packagesFolder);
	if (!exists(packagesFolder)) mkdir(packagesFolder);
	
	if (exists(buildPath(getcwd(), "dub.json")))
	{
		dubFile = buildPath(getcwd(), "dub.json");
		dubArgs = args[1..$];
		auto jsRoot = parseJSON(readText(dubFile));
		if (processJson(jsRoot, packagesFolder))
		{
			copy(dubFile, dubFile~".old");
			writeTextFile(dubFile, jsRoot.toString);
		}
	}
	else if (args.length > 1 && buildPath(getcwd(), 
args[1].stripExtension~".d").exists)
	{
		enum marker = "dub.json:";
		dubFile = buildPath(getcwd(), args[1].stripExtension~".d");
		dubArgs = dubFile~args[2..$];
		string sourceCode = readText(dubFile);
		auto startPos = sourceCode.indexOf("/+");
		auto markerPos = sourceCode.indexOf(marker);
		auto endPos =  sourceCode.indexOf("+/");
		if (startPos > -1 && markerPos > startPos && markerPos < endPos)
		{
			auto jsRoot = 
parseJSON(sourceCode[markerPos+marker.length..endPos]);
			if (processJson(jsRoot, packagesFolder))
			{
				sourceCode = sourceCode[0..markerPos+marker.length]~" 
"~jsRoot.toString~" "~sourceCode[endPos..$];
				copy(dubFile, dubFile~".old");
				writeTextFile(dubFile, sourceCode);
			}
		}
	}
	import std.stdio: writeln;
	with (execute(["dub"]~dubArgs)) writeln(output);
}

private bool processJson(JSONValue jsRoot, string packagesFolder)
{
	bool found = processDependencies(jsRoot, packagesFolder);
	if ("configurations" in jsRoot && jsRoot["configurations"].type 
== JSON_TYPE.ARRAY)
			found = jsRoot["configurations"].array.any!(jsConfig => 
processDependencies(jsConfig, packagesFolder)) || found;
	return found;
}

private bool processDependencies(JSONValue js, string 
packagesFolder)
{
	bool result;
	if ("dependencies" in js && js["dependencies"].type == 
JSON_TYPE.OBJECT)
	{
		foreach(key; js["dependencies"].object.keys)
		{
			auto dep = js["dependencies"].object[key];
			if (dep.type == JSON_TYPE.OBJECT && "url" in dep && 
dep["url"].type == JSON_TYPE.STRING)
			{
				string url = dep["url"].str;
				string fileName = url.split("/").array[$-1];
				string destination = buildPath(packagesFolder, fileName);
				
				if (exists(destination))
				{
					dep["path"] = buildPath(destination.dirName, 
destination.stripExtension);
				}
				else
				{
					try	{ dep["path"] = downloadAndExtract(url, destination); }
					catch(Exception e)
					{
						error(e.msg);
						continue;
					}
				}
				dep.object.remove("url");
				result = true;
			}
			else if (dep.type == JSON_TYPE.OBJECT && "path" in dep && 
dep["path"].type == JSON_TYPE.STRING && 
dep["path"].str.toLower.endsWith(".zip"))
			{
				string filePath = dep["path"].str;
				string packageFolder = buildPath(packagesFolder, 
filePath.baseName.stripExtension);

				if (!exists(packageFolder))
					with (execute(["unzip", "-o", filePath, "-d", 
packagesFolder])) enforce(status == 0, output);

				dep["path"] = packageFolder;	
				result = true;
			}
		}
	}
	return result;
}

private string downloadAndExtract(string url, string destination)
{
	info("download: ", url, " to ", destination);
	download(url, destination);
	enforce(destination.toLower.endsWith(".zip"), "Unknown file 
extenstion");
	with (execute(["unzip", destination, "-d", 
destination.dirName])) enforce(status == 0, output);
	return buildPath(destination.dirName, 
destination.baseName!(std.path.CaseSensitive.no)(".zip"));
}

private void writeTextFile(string filePath, string content)
{
	import std.stdio: File;
	auto f = File(filePath, "wb");
	f.write(content);
     f.close();
}

--

Kind regards
André
May 17