diff --git a/bin/dub b/bin/dub index 79daca0..34fe490 100644 --- a/bin/dub +++ b/bin/dub @@ -2,12 +2,14 @@ set -e # delete old dub.d if another run left it in /tmp -rm -f /tmp/dub.d +rm -f /tmp/app.d # find the executable location (note: must stay mac compatible here) -VIBEBINARY=$(readlink "$0" || true) -if [ ! -n "$VIBEBINARY" ]; then VIBEBINARY="$0"; fi -VIBEPATH=$(dirname "$VIBEBINARY") +DUB_BINARY=$(readlink "$0" || true) +if [ ! -n "$DUB_BINARY" ]; then DUB_BINARY="$0"; fi +DUB_BIN=$(dirname "$DUB_BINARY") +DUB_SOURCE=$DUB_BIN/../source +VIBE_SOURCE=$DUB_SOURCE/../../vibe.d/source # use pkg-config if possible or fallback to default flags LIBS=$(pkg-config --libs libevent libevent_pthreads libssl 2>/dev/null || echo "-levent_pthreads -levent -lssl -lcrypto") @@ -18,12 +20,12 @@ START_SCRIPT=`mktemp -t dub.start.XXXXXXXX` # copy dub.d to /tmp and make it deletable by anyone -cp -p "$VIBEPATH"/dub.d /tmp/dub.d -chmod 666 /tmp/dub.d +cp -p "$DUB_BIN"/app.d /tmp/app.d +chmod 666 /tmp/app.d # run VPM and delete the dub.d file again, VPM will output the compile/run script -rdmd -g -w -property -I"$VIBEPATH"/../source $LIBS -Jviews -Isource /tmp/dub.d "$VIBEPATH" "$START_SCRIPT" $1 $2 $3 $4 $5 $6 $7 $8 $9 -rm /tmp/dub.d +rdmd -g -w -property -I"$DUB_SOURCE" -I"$VIBE_SOURCE" $LIBS -Jviews -Isource /tmp/app.d "$VIBE_SOURCE" "$START_SCRIPT" $1 $2 $3 $4 $5 $6 $7 $8 $9 +rm /tmp/app.d # compile/run the application chmod +x "$START_SCRIPT" diff --git a/bin/dub.cmd b/bin/dub.cmd index 70e432a..0146cca 100644 --- a/bin/dub.cmd +++ b/bin/dub.cmd @@ -1,7 +1,9 @@ @echo off -set VIBE_BIN=%~dps0 -set LIBDIR=%VIBE_BIN%..\lib\win-i386 -set BINDIR=%VIBE_BIN%..\lib\bin +set DUB_BIN=%~dps0 +set DUB_SOURCE=%DUB_BIN%..\source +set VIBE_SOURCE=%DUB_BIN%..\..\vibe.d\source +set LIBDIR=%VIBE_SOURCE%\..\lib\win-i386 +set BINDIR=%DUB_BIN%..\lib\bin set LIBS="%LIBDIR%\event2.lib" "%LIBDIR%\eay.lib" "%LIBDIR%\ssl.lib" ws2_32.lib set EXEDIR=%TEMP%\.rdmd\source set START_SCRIPT=%EXEDIR%\vibe.cmd @@ -9,12 +11,12 @@ if NOT EXIST %EXEDIR% ( mkdir %EXEDIR% ) -copy "%VIBE_BIN%*.dll" %EXEDIR% > nul 2>&1 -if "%1" == "build" copy "%VIBE_BIN%*.dll" . > nul 2>&1 -copy "%VIBE_BIN%dub.d" %EXEDIR% > nul 2>&1 +copy "%DUB_BIN%*.dll" %EXEDIR% > nul 2>&1 +if "%1" == "build" copy "%DUB_BIN%*.dll" . > nul 2>&1 +copy "%DUB_SOURCE%\app.d" %EXEDIR% > nul 2>&1 -rem Run, execute, do everything.. but when you do it, do it with the vibe! -rdmd -debug -g -w -property -of%EXEDIR%\dub.exe -I%VIBE_BIN%..\source %LIBS% %EXEDIR%\dub.d %VIBE_BIN% %START_SCRIPT% %* +rem Run, execute, do everything.. +rdmd -debug -g -w -property -of%EXEDIR%\dub.exe -I%DUB_SOURCE% -I%VIBE_SOURCE% %LIBS% %EXEDIR%\app.d %VIBE_SOURCE% %START_SCRIPT% %* rem Finally, start the app, if dub succeded. if ERRORLEVEL 0 %START_SCRIPT% diff --git a/bin/dub.d b/bin/dub.d deleted file mode 100644 index 6e092a6..0000000 --- a/bin/dub.d +++ /dev/null @@ -1,298 +0,0 @@ -/** - 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 [] [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 [] [] [-- ] - -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 [] 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 0) { - logError("The dependency graph could not be filled."); - Action[] actions; - foreach( string pkg, rdp; graph.missing()) - actions ~= Action(Action.ActionId.Failure, pkg, rdp.dependency, rdp.packages); - return actions; - } - - auto conflicts = graph.conflicted(); - if(conflicts.length > 0) { - logDebug("Conflicts found"); - Action[] actions; - foreach( string pkg, dbp; conflicts) - actions ~= Action(Action.ActionId.Conflict, pkg, dbp.dependency, dbp.packages); - return actions; - } - - // Gather installed - Package[string] installed; - installed[m_main.name] = m_main; - foreach(string pkg, ref Package p; m_packages) { - enforce( pkg !in installed, "The package '"~pkg~"' is installed more than once." ); - installed[pkg] = p; - } - - // To see, which could be uninstalled - Package[string] unused = installed.dup; - unused.remove( m_main.name ); - - // Check against installed and add install actions - Action[] actions; - Action[] uninstalls; - foreach( string pkg, d; graph.needed() ) { - auto p = pkg in installed; - // TODO: auto update to latest head revision - if(!p || (!d.dependency.matches(p.vers) && !d.dependency.matches(Version.MASTER))) { - if(!p) logDebug("Application not complete, required package '"~pkg~"', which was not found."); - else logDebug("Application not complete, required package '"~pkg~"', invalid version. Required '%s', available '%s'.", d.dependency, p.vers); - actions ~= Action(Action.ActionId.InstallUpdate, pkg, d.dependency, d.packages); - } else { - logDebug("Required package '"~pkg~"' found with version '"~p.vers~"'"); - if( option & UpdateOptions.Reinstall ) { - Dependency[string] em; - uninstalls ~= Action( Action.ActionId.Uninstall, pkg, new Dependency("==" ~ p.vers), em); - actions ~= Action(Action.ActionId.InstallUpdate, pkg, d.dependency, d.packages); - } - - if( (pkg in unused) !is null ) - unused.remove(pkg); - } - } - - // Add uninstall actions - foreach( string pkg, p; unused ) { - logDebug("Superfluous package found: '"~pkg~"', version '"~p.vers~"'"); - Dependency[string] em; - uninstalls ~= Action( Action.ActionId.Uninstall, pkg, new Dependency("==" ~ p.vers), em); - } - - // Ugly "uninstall" comes first - actions = uninstalls ~ actions; - - return actions; - } - - void createZip(string destination) { - assert(false); // not properly implemented - /* - string[] ignores; - auto ignoreFile = to!string(m_root~"vpm.ignore.txt"); - if(exists(ignoreFile)){ - auto iFile = openFile(ignoreFile); - scope(exit) iFile.close(); - while(!iFile.empty) - ignores ~= to!string(cast(char[])iFile.readLine()); - logDebug("Using '%s' found by the application.", ignoreFile); - } - else { - ignores ~= ".svn/*"; - ignores ~= ".git/*"; - ignores ~= ".hg/*"; - logDebug("The '%s' file was not found, defaulting to ignore:", ignoreFile); - } - ignores ~= "modules/*"; // modules will not be included - foreach(string i; ignores) - logDebug(" " ~ i); - - logDebug("Creating zip file from application: " ~ m_main.name); - auto archive = new ZipArchive(); - foreach( string file; dirEntries(to!string(m_root), SpanMode.depth) ) { - enforce( Path(file).startsWith(m_root) ); - auto p = Path(file); - p = p[m_root.length..p.length]; - if(isDir(file)) continue; - foreach(string ignore; ignores) - if(globMatch(file, ignore)) - would work, as I see it; - continue; - logDebug(" Adding member: %s", p); - ArchiveMember am = new ArchiveMember(); - am.name = to!string(p); - auto f = openFile(file); - scope(exit) f.close(); - am.expandedData = f.readAll(); - archive.addMember(am); - } - - logDebug(" Writing zip: %s", destination); - auto dst = openFile(destination, FileMode.CreateTrunc); - scope(exit) dst.close(); - dst.write(cast(ubyte[])archive.build()); - */ - } - - private bool gatherMissingDependencies(PackageSupplier packageSupplier, DependencyGraph graph) { - RequestedDependency[string] missing = graph.missing(); - RequestedDependency[string] oldMissing; - while( missing.length > 0 ) { - if(missing.length == oldMissing.length) { - bool different = false; - foreach(string pkg, reqDep; missing) { - auto o = pkg in oldMissing; - if(o && reqDep.dependency != o.dependency) { - different = true; - break; - } - } - if(!different) { - logWarn("Could not resolve dependencies"); - return false; - } - } - - oldMissing = missing.dup; - logTrace("There are %s packages missing.", missing.length); - foreach(string pkg, reqDep; missing) { - if(!reqDep.dependency.valid()) { - logTrace("Dependency to "~pkg~" is invalid. Trying to fix by modifying others."); - continue; - } - - // TODO: auto update and update interval by time - logTrace("Adding package to graph: "~pkg); - Package p = null; - - // Try an already installed package first - if(!needsUpToDateCheck(pkg)) { - try { - auto json = jsonFromFile( m_root ~ Path("modules") ~ Path(pkg) ~ "package.json"); - auto vers = Version(json["version"].get!string); - if( reqDep.dependency.matches( vers ) ) - p = new Package(json); - logTrace("Using already installed package with version: %s", vers); - } - catch(Throwable e) { - // not yet installed, try the supplied PS - logTrace("An installed package was not found"); - } - } - if(!p) { - try { - p = new Package(packageSupplier.packageJson(pkg, reqDep.dependency)); - logTrace("using package from registry"); - markUpToDate(pkg); - } - catch(Throwable e) { - logError("Geting package metadata for %s failed, exception: %s", pkg, e.toString()); - } - } - - if(p) - graph.insert(p); - } - graph.clearUnused(); - missing = graph.missing(); - } - - return true; - } - - private bool needsUpToDateCheck(string packageId) { - try { - auto time = m_json["vpm"]["lastUpdate"][packageId].to!string; - return (Clock.currTime() - SysTime.fromISOExtString(time)) > dur!"days"(1); - } - catch(Throwable t) { - return true; - } - } - - private void markUpToDate(string packageId) { - logTrace("markUpToDate(%s)", packageId); - Json create(ref Json json, string object) { - if( object !in json ) json[object] = Json.EmptyObject; - return json[object]; - } - create(m_json, "vpm"); - create(m_json["vpm"], "lastUpdate"); - m_json["vpm"]["lastUpdate"][packageId] = Json( Clock.currTime().toISOExtString() ); - - writeVpmJson(); - } - - private void writeVpmJson() { - // don't bother to write an empty file - if( m_json.length == 0 ) return; - - try { - logTrace("writeVpmJson"); - auto dstFile = openFile((m_root~"vpm.json").toString(), FileMode.CreateTrunc); - scope(exit) dstFile.close(); - Appender!string js; - toPrettyJson(js, m_json); - dstFile.write( js.data ); - } catch( Exception e ){ - logWarn("Could not write vpm.json."); - } - } -} - -/// The default supplier for packages, which is the registry -/// hosted by vibed.org. -PackageSupplier defaultPackageSupplier() { - Url url = Url.parse("http://registry.vibed.org/"); - logDebug("Using the registry from %s", url); - return new RegistryPS(url); -} - -enum UpdateOptions -{ - None = 0, - JustAnnotate = 1<<0, - Reinstall = 1<<1 -}; - -/// The Vpm or Vibe Package Manager helps in getting the applications -/// dependencies up and running. -class Vpm { - private { - Path m_root; - Application m_app; - PackageSupplier m_packageSupplier; - } - - /// Initiales the package manager for the vibe application - /// under root. - this(Path root, PackageSupplier ps = defaultPackageSupplier()) { - enforce(root.absolute, "Specify an absolute path for the VPM"); - m_root = root; - m_packageSupplier = ps; - m_app = new Application(root); - } - - /// Returns the name listed in the package.json of the current - /// application. - @property string packageName() const { return m_app.name; } - - /// Returns a list of flags which the application needs to be compiled - /// properly. - @property string[] dflags() { return m_app.dflags; } - - /// Lists all installed modules - void list() { - logInfo(m_app.info()); - } - - /// Performs installation and uninstallation as necessary for - /// the application. - /// @param options bit combination of UpdateOptions - bool update(UpdateOptions options) { - Action[] actions = m_app.actions(m_packageSupplier, options); - if( actions.length == 0 ) return true; - - logInfo("The following changes could be performed:"); - bool conflictedOrFailed = false; - foreach(Action a; actions) { - logInfo(capitalize( to!string( a.action ) ) ~ ": " ~ a.packageId ~ ", version %s", a.vers); - if( a.action == Action.ActionId.Conflict || a.action == Action.ActionId.Failure ) { - logInfo("Issued by: "); - conflictedOrFailed = true; - foreach(string pkg, d; a.issuer) - logInfo(" "~pkg~": %s", d); - } - } - - if( conflictedOrFailed || options & UpdateOptions.JustAnnotate ) - return conflictedOrFailed; - - // Uninstall first - - // ?? - // foreach(Action a ; filter!((Action a) => a.action == Action.ActionId.Uninstall)(actions)) - // uninstall(a.packageId); - // foreach(Action a; filter!((Action a) => a.action == Action.ActionId.InstallUpdate)(actions)) - // install(a.packageId, a.vers); - foreach(Action a; actions) - if(a.action == Action.ActionId.Uninstall) - uninstall(a.packageId); - foreach(Action a; actions) - if(a.action == Action.ActionId.InstallUpdate) - install(a.packageId, a.vers); - - m_app.reinit(); - Action[] newActions = m_app.actions(m_packageSupplier, 0); - if(newActions.length > 0) { - logInfo("There are still some actions to perform:"); - foreach(Action a; newActions) - logInfo("%s", a); - } - else - logInfo("You are up to date"); - - return newActions.length == 0; - } - - /// Creates a zip from the application. - void createZip(string zipFile) { - m_app.createZip(zipFile); - } - - /// Prints some information to the log. - void info() { - logInfo("Status for %s", m_root); - logInfo("\n" ~ m_app.info()); - } - - /// Gets all installed packages as a "packageId" = "version" associative array - string[string] installedPackages() const { return m_app.installedPackages(); } - - /// Installs the package matching the dependency into the application. - /// @param addToApplication if true, this will also add an entry in the - /// list of dependencies in the application's package.json - void install(string packageId, const Dependency dep, bool addToApplication = false) { - logInfo("Installing "~packageId~"..."); - auto destination = m_root ~ "modules" ~ packageId; - if(exists(to!string(destination))) - throw new Exception(packageId~" needs to be uninstalled prior installation."); - - // download - ZipArchive archive; - { - logDebug("Aquiring package zip file"); - auto dload = m_root ~ "temp/downloads"; - if(!exists(to!string(dload))) - mkdirRecurse(to!string(dload)); - auto tempFile = m_root ~ ("temp/downloads/"~packageId~".zip"); - string sTempFile = to!string(tempFile); - if(exists(sTempFile)) remove(sTempFile); - m_packageSupplier.storePackage(tempFile, packageId, dep); // Q: continue on fail? - scope(exit) remove(sTempFile); - - // unpack - auto f = openFile(to!string(tempFile), FileMode.Read); - scope(exit) f.close(); - ubyte[] b = new ubyte[cast(uint)f.leastSize]; - f.read(b); - archive = new ZipArchive(b); - } - - Path getPrefix(ZipArchive a) { - foreach(ArchiveMember am; a.directory) - if( Path(am.name).head == PathEntry("package.json") ) - return Path(am.name).parentPath; - - // not correct zip packages HACK - Path minPath; - foreach(ArchiveMember am; a.directory) - if( isPathFromZip(am.name) && (minPath == Path() || minPath.startsWith(Path(am.name))) ) - minPath = Path(am.name); - - return minPath; - } - - logDebug("Installing from zip."); - - // In a github zip, the actual contents are in a subfolder - auto prefixInPackage = getPrefix(archive); - - Path getCleanedPath(string fileName) { - auto path = Path(fileName); - if(prefixInPackage != Path() && !path.startsWith(prefixInPackage)) return Path(); - return path[prefixInPackage.length..path.length]; - } - - // install - mkdirRecurse(to!string(destination)); - Journal journal = new Journal; - foreach(ArchiveMember a; archive.directory) { - if(!isPathFromZip(a.name)) continue; - - auto cleanedPath = getCleanedPath(a.name); - if(cleanedPath.empty) continue; - auto fileName = to!string(destination~cleanedPath); - - if( exists(fileName) && isDir(fileName) ) continue; - - logDebug("Creating %s", fileName); - mkdirRecurse(fileName); - auto subPath = cleanedPath; - for(size_t i=0; i a.type == Journal.Type.RegularFile)(journal.entries)) { - logTrace("Deleting file '%s'", e.relFilename); - auto absFile = packagePath~e.relFilename; - if(!exists(to!string(absFile))) { - logWarn("Previously installed file not found for uninstalling: '%s'", absFile); - continue; - } - - remove(to!string(absFile)); - } - - logDebug("Erasing directories"); - Path[] allPaths; - foreach(Journal.Entry e; filter!((Journal.Entry a) => a.type == Journal.Type.Directory)(journal.entries)) - allPaths ~= packagePath~e.relFilename; - sort!("a.length>b.length")(allPaths); // sort to erase deepest paths first - foreach(Path p; allPaths) { - logTrace("Deleting folder '%s'", p); - if( !exists(to!string(p)) || !isDir(to!string(p)) || !isEmptyDir(p) ) { - logError("Alien files found, directory is not empty or is not a directory: '%s'", p); - continue; - } - rmdir( to!string(p) ); - } - - if(!isEmptyDir(packagePath)) - throw new Exception("Alien files found in '"~to!string(packagePath)~"', manual uninstallation needed."); - - rmdir(to!string(packagePath)); - logInfo("Uninstalled package: '"~packageId~"'"); - } -}