Newer
Older
dub_jkp / bin / dub.d
@Sönke Ludwig Sönke Ludwig on 2 Nov 2012 8 KB Renamed the files in the bin directory.
/**
	The entry point to vibe.d

	Copyright: © 2012 Matthias Dondorff
	License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
	Authors: Matthias Dondorff
*/
module dub;

import vibe.core.file;
import vibe.core.log;
import vibe.inet.url;
import vibe.dub.dub;
import vibe.dub.registry;
import vibe.utils.string;

import std.algorithm;
import std.array;
import std.conv;
import std.exception;
import std.file;
import std.getopt;
import std.process;


int main(string[] args)
{
	string cmd;

	try {
		if( args.length < 3 ){
			logError("Usage: %s <vibe-binary-path> <start-script-output-file> [<command>] [args...] [-- [applicatio args]]\n", args[0]);
			// vibe-binary-path: the installation folder of the vibe installation
			// start-script-output-file: destination of the script, which can be used to run the app
			return 1;
		}

		// parse general options
		bool verbose, vverbose, quiet, vquiet;
		bool help, nodeps, annotate;
		LogLevel loglevel = LogLevel.Info;
		getopt(args,
			"v|verbose", &verbose,
			"vverbose", &vverbose,
			"q|quiet", &quiet,
			"vquiet", &vquiet,
			"h|help", &help,
			"nodeps", &nodeps,
			"annotate", &annotate
			);

		if( vverbose ) loglevel = LogLevel.Trace;
		else if( verbose ) loglevel = LogLevel.Debug;
		else if( vquiet ) loglevel = LogLevel.None;
		else if( quiet ) loglevel = LogLevel.Warn;
		setLogLevel(loglevel);
		if( loglevel >= LogLevel.Info ) setPlainLogging(true);


		// extract the destination paths
		enforce(isDir(args[1]), "Specified binary path is not a directory.");
		Path vibedDir = Path(args[1]);
		Path dstScript = Path(args[2]);

		// extract the command
		if( args.length > 3 && !args[3].startsWith("-") ){
			cmd = args[3];
			args = args[0] ~ args[4 .. $];
		} else {
			cmd = "run";
			args = args[0] ~ args[3 .. $];
		}

		// contrary to the documentation, getopt does not remove --
		if( args.length >= 2 && args[1] == "--" ) args = args[0] ~ args[2 .. $];

		// display help if requested
		if( help ){
			showHelp(cmd);
			return 0;
		}

		auto appPath = getcwd();
		string appStartScript;
		Url registryUrl = Url.parse("http://registry.vibed.org/");
		logDebug("Using dub registry url '%s'", registryUrl);

		// handle the command
		switch( cmd ){
			default:
				enforce(false, "Command is unknown.");
				assert(false);
			case "init":
				string dir = ".";
				if( args.length >= 2 ) dir = args[1];
				initDirectory(dir);
				break;
			case "run":
			case "build":
				Vpm dub = new Vpm(Path(appPath), new RegistryPS(registryUrl));
				if( !nodeps ){
					logInfo("Checking dependencies in '%s'", appPath);
					logDebug("dub initialized");
					dub.update(annotate ? UpdateOptions.JustAnnotate : UpdateOptions.None);
				}

				//Added check for existance of [AppNameInPackagejson].d
				//If exists, use that as the starting file.
				string binName = getBinName(dub);
				version(Windows) { string appName = binName[0..$-4]; 	}
				version(Posix)   { string appName = binName; 			}

				logDebug("Application Name is '%s'", binName);

				// Create start script, which will be used by the calling bash/cmd script.
				// build "rdmd --force %DFLAGS% -I%~dp0..\source -Jviews -Isource @deps.txt %LIBS% source\app.d" ~ application arguments
				// or with "/" instead of "\"
				string[] flags = ["--force"];
				if( cmd == "build" ){
					flags ~= "--build-only";
					flags ~= "-of"~binName;
				}
				flags ~= "-g";
				flags ~= "-I" ~ (vibedDir ~ ".." ~ "source").toNativeString();
				flags ~= "-Isource";
				flags ~= "-Jviews";
				flags ~= dub.dflags;
				flags ~= getLibs(vibedDir);
				flags ~= getPackagesAsVersion(dub);
				flags ~= (Path("source") ~ appName).toNativeString();
				flags ~= args[1 .. $];

				appStartScript = "rdmd " ~ getDflags() ~ " " ~ join(flags, " ");
				break;
			case "upgrade":
				logInfo("Upgrading application in '%s'", appPath);
				Vpm dub = new Vpm(Path(appPath), new RegistryPS(registryUrl));
				logDebug("dub initialized");
				dub.update(UpdateOptions.Reinstall | (annotate ? UpdateOptions.JustAnnotate : UpdateOptions.None));
				break;
		}

		auto script = openFile(to!string(dstScript), FileMode.CreateTrunc);
		scope(exit) script.close();
		script.write(appStartScript);

		return 0;
	}
	catch(Throwable e)
	{
		logError("Error executing command '%s': %s\n", cmd, e.msg);
		logDebug("Full exception: %s", sanitizeUTF8(cast(ubyte[])e.toString()));
		showHelp(cmd);
		return -1;
	}
}


private void showHelp(string command)
{
	// This help is actually a mixup of help for this application and the
	// supporting dub script / .cmd file.
	logInfo(
"Usage: dub [<command>] [<vibe options...>] [-- <application options...>]

Manages the vibe.d application in the current directory. A single -- can be used
to separate vibe options from options passed to the application.

Possible commands:
    init [<directory>]   Initializes an empy project in the specified directory
    run                  Compiles and runs the application
    build                Just compiles the application in the project directory
    upgrade              Forces an upgrade of all dependencies

Options:
    -v  --verbose        Also output debug messages
        --vverbose       Also output trace messages (produces a lot of output)
    -q  --quiet          Only output warnings and errors
        --vquiet         No output
    -h  --help           Print this help screen
        --nodeps         Do not check dependencies for 'run' or 'build'
        --annotate       Do not execute dependency installations, just print
");
}


private string getDflags()
{
	auto globVibedDflags = environment.get("DFLAGS");
	if(globVibedDflags == null)
		globVibedDflags = "-debug -g -w -property";
	return globVibedDflags;
}

private string[] getLibs(Path vibedDir)
{
	version(Windows)
	{
		auto libDir = vibedDir ~ "..\\lib\\win-i386";
		return ["ws2_32.lib",
			(libDir ~ "event2.lib").toNativeString(),
			(libDir ~ "eay.lib").toNativeString(),
			(libDir ~ "ssl.lib").toNativeString()];
	}
	version(Posix)
	{
		return split(environment.get("LIBS", "-L-levent_openssl -L-levent"));
	}
}

private string stripDlangSpecialChars(string s) 
{
	char[] ret = s.dup;
	for(int i=0; i<ret.length; ++i)
		if(!isAlpha(ret[i]))
			ret[i] = '_';
	return to!string(ret);
}

private string[] getPackagesAsVersion(const Vpm dub)
{
	string[] ret;
	string[string] pkgs = dub.installedPackages();
	foreach(id, vers; pkgs)
		ret ~= "-version=VPM_package_" ~ stripDlangSpecialChars(id);
	return ret;
}

private string getBinName(const Vpm dub)
{
	string ret;
	if(existsFile(Path("source") ~ (dub.packageName() ~ ".d")))
		ret = dub.packageName();
	//Otherwise fallback to source/app.d
	else
		ret = (Path(".") ~ "app").toNativeString();
	version(Windows) { ret ~= ".exe"; }

	return ret;
} 

private void initDirectory(string fName)
{ 
    Path cwd; 
    //Check to see if a target directory is specified.
    if(fName != ".") {
        if(!existsFile(fName))  
            createDirectory(fName);
        cwd = Path(fName);  
    } 
    //Otherwise use the current directory.
    else 
        cwd = Path("."); 
    
    //raw strings must be unindented. 
    immutable packageJson = 
`{
    "name": "`~(fName == "." ? "my-project" : fName)~`",
    "version": "0.0.1",
    "description": "An example project skeleton",
    "homepage": "http://example.org",
    "copyright": "Copyright © 2000, Edit Me",
    "authors": [
        "Your Name"
    ],
    "dependencies": {
    }
}
`;
    immutable appFile =
`import vibe.d;

static this()
{ 
    logInfo("Edit source/app.d to start your project.");
}
`;
	//Make sure we do not overwrite anything accidentally
	if( (existsFile(cwd ~ "package.json"))        ||
		(existsFile(cwd ~ "source"      ))        ||
		(existsFile(cwd ~ "views"       ))        || 
		(existsFile(cwd ~ "public"     )))
	{
		logInfo("The current directory is not empty.\n"
				"vibe init aborted.");
		//Exit Immediately. 
		return;
	}
	//Create the common directories.
	createDirectory(cwd ~ "source");
	createDirectory(cwd ~ "views" );
	createDirectory(cwd ~ "public");
	//Create the common files. 
	openFile(cwd ~ "package.json", FileMode.Append).write(packageJson);
	openFile(cwd ~ "source/app.d", FileMode.Append).write(appFile);     
	//Act smug to the user. 
	logInfo("Successfully created empty project.");
}