func Tesses.CrossLang.Shell.InstallWebApp(dd)
{
    //crosslang install-webapp Tesses.CrossLang.WebApp.SomeWebApp --version=1.0.0.0-prod
    var toolsDir = Env.CrossLangConfig / "WebApps";
            
    var offline=false;
    var buildPath = ".";
    var nobuild=false;
    var output=.;   
    each(var flag : dd.Flags)
    {
        if(flag == "offline")
        {
            offline = true;
        }
        else if(flag == "help")
        {
            Console.WriteLine("USAGE: crosslang install-webapp [flags-and-options]");
            Console.WriteLine("USAGE: crosslang install-webapp [PackageName] [flags-and-options]");
            Console.WriteLine("FLAGS:");
            Console.WriteLine("offline:  build with no internet (don't fetch files)");
            Console.WriteLine("help:     this help");
            Console.WriteLine("nobuild:  don't build, just install (irrelevant if you specify a PackageName)");
            Console.WriteLine();
            Console.WriteLine("OPTIONS:");
            Console.WriteLine("conf=CONFIGSTRING:     specify a conf string for compile_tool(s), is the property Config (irrelevant if you specify a PackageName)");
            Console.WriteLine("version=1.0.0.0-prod:  specify the package version (irrelevant if you don't specify a PackageName)");
            Console.WriteLine();
            Console.WriteLine("ARGUMENTS:");
            Console.WriteLine("PackageName: the package name of a web application you want to download and install, if not specified we install the web application from the current directory");
            return 0;
        }
        else if(flag == "nobuild")
        {
            nobuild=true;
        }
    }
    if(dd.Arguments.Length == 2)
	{
		var pm = new Tesses.CrossLang.PackageManager();
        pm.Offline = offline;
		var name = dd.Arguments[1];
		var version = null;
        each(var opt : dd.Options)
        {
        	if(opt.Key == "version")
             	version = opt.Value;
        }
		if(version == null)
        {
            var pm = new Tesses.CrossLang.PackageManager();
            version = pm.GetLatest(name);
        }
        if(version == null)
        {
        	Console.WriteLine("Could not get version");
            return 1;
        }
		var ms = new MemoryStream(true);
		var ba = pm.GetPackage(name, version);
		ms.WriteBlock(ba,0,ba.Length);
		ms.Seek(0,0);
		var exec = VM.LoadExecutable(ms);
		var info = Json.Decode(exec.Info);
		if(info.type != "webapp")
		{
			Console.WriteLine("The project is not a web application");
			ms.Close();
			return 1;
		}
		
		if(TypeOf(info.short_name) == "String")
		{
			var dir = toolsDir / info.short_name;
			if(FS.Local.DirectoryExists(dir))
			{
				each(var item : FS.Local.EnumeratePaths(dir))
				{
					if(FS.Local.FileExists(item) && item.GetExtension() == ".crvm")
					{
						FS.Local.DeleteFile(item);
					}
				}
			}

			FS.Local.CreateDirectory(dir);
		
			FS.WriteAllBytes(FS.Local, dir / info.short_name + ".crvm", ba);

			func downloadDependency(_name, _version)
			{
				var ms = new MemoryStream(true);
				var ba = pm.GetPackage(_name, _version);
                
				ms.WriteBlock(ba,0,ba.Length);
				ms.Seek(0,0);
				var exec = VM.LoadExecutable(ms);
				var execPath = dir / $"{_name}-{_version}.crvm";
				
				if(!FS.Local.FileExists(execPath))
					FS.WriteAllBytes(FS.Local, execPath , ba);

				each(var item : exec.Dependencies)
					downloadDependency(item.Name, item.Version.ToString());
				ms.Close();
			}

			each(var item : exec.Dependencies)
				downloadDependency(item.Name, item.Version.ToString());
			Console.WriteLine($"Installed web application {info.short_name}");
		}

		ms.Close();
		
	}
    else if(dd.Arguments.Length == 1)
    {
                
        var conf = "";
        each(var option : dd.Options)
        {
            if(option.Key == "conf")
            {
                conf = option.Value;
            }
        }

        if(FS.Local.FileExists("cross.json"))
        {
            var proj = Json.Decode(FS.ReadAllText(FS.Local,"cross.json"));
            var name = proj.info.short_name;
                    
            if(proj.info.type != "webapp")
            {
                Console.WriteLine("The project is not a web application");
                return 1;
            }
            if(TypeOf(name) != "String")
            {
                Console.WriteLine("The web application does not have a name");
                return 1;
            }
            var toolDir = toolsDir / name;
            FS.Local.CreateDirectory(toolDir);
            var toolFile = toolDir / name + ".crvm";

            if(nobuild)
            {
                var nameVer = $"{proj.name}-{proj.version}.crvm";
                var buildDir = TypeOf(proj.bin_directory) == "String" ? proj.bin_directory : "bin";
                    
                output = ./buildDir/nameVer;
                if(!FS.Local.FileExists(output))
                {
                    Console.WriteLine($"ERROR: file {output} does not exist.");
                    return 1;
                }
				
            } 
            else
            {
                var pm = new Tesses.CrossLang.PackageManager();
                pm.Offline = offline;
                var bt = new Tesses.CrossLang.BuildTool(pm);
                bt.Config = conf;
                output =  bt.BuildProject(buildPath).Output;
            }
                    
            func CopyCRVM(src,dest)
            {
                func copyFile(src,dest)
                {
                    var _src = FS.Local.OpenFile(src,"rb");
                    var _dest = FS.Local.OpenFile(dest, "wb");
                    _src.CopyTo(_dest);
                    _src.Close();
                    _dest.Close();
                    Console.WriteLine($"{src} -> {dest}");
                }    
                if(FS.Local.FileExists(dest)) return;
                    copyFile(src,dest);

                        
                var srcStrm = FS.Local.OpenFile(src,"rb");
                var crvm = VM.LoadExecutable(srcStrm);
                        
                srcStrm.Close();
                each(var dep : crvm.Dependencies)
                {
                    var name = $"{dep.Name}-{dep.Version.ToString()}.crvm";
                    CopyCRVM(src.GetParent()/name, dest.GetParent()/name);
                }
            }
            CopyCRVM(output,toolFile);
            return 0;
        }
        else
        {
                    
            Console.WriteLine("The current directory does not have a project");
            return 1;
        }
    }
    return 0;
}