Example #1
0
		/// <summary>
		/// Compiles the Html Help 2 project
		/// </summary>
		/// <param name="projectRoot">The root locaion of the project inputs</param>
		/// <param name="helpName">The name of the project</param>
		/// <param name="langID">The language ID of the final help file</param>
		public void Compile( DirectoryInfo projectRoot, string helpName, short langID )
		{
			string ProjectPathAndName = Path.Combine( projectRoot.FullName, helpName );

			using( CompilerStatus status = new CompilerStatus( ProjectPathAndName + ".HxComp.log" ) )
			{ 
				int cookie = -1;
				HxCompClass compiler = new HxCompClass();

				compiler.Initialize();
				compiler.LangId = langID;

				cookie = compiler.AdviseCompilerMessageCallback( status );

				compiler.Compile( ProjectPathAndName + ".HxC", projectRoot.FullName, helpName + ".HxS", 0 );
			
				if ( cookie != -1 )
					compiler.UnadviseCompilerMessageCallback( cookie );

				if ( status.CompileFailed )
					throw new Exception( status.ErrorMessage );

				Trace.WriteLine( "Help title compile complete" );
			}

		}
        //deletes leftovers and checks current status
        void InitCompiler()
        {
            var rldt = RelativePath(LiveDirTest);

            if (File.Exists(rldt))
            {
                File.Delete(rldt);
            }
            compilerCurrentStatus = IsInitialized();
        }
Example #3
0
        /// <summary>
        /// Compiles the code with the specified reference assemblies into a dll file.
        /// </summary>
        /// <param name="ReferenceAssemblies">The reference assemblies.</param>
        /// <param name="Code">The code.</param>
        public static void Compile(string[] ReferenceAssemblies, string Code)
        {
            DeleteOldDll();
            CodeHost.Stop();
            #if !DEBUG
            param.CompilerOptions = @"/optimize";
            #endif
            //add user references
            param.ReferencedAssemblies.AddRange(ReferenceAssemblies);

            //add our references
            //for output support
            param.ReferencedAssemblies.Add("API.dll");
            //for input support
            param.ReferencedAssemblies.Add("InputLib.dll");
            //for joystick support
            param.ReferencedAssemblies.Add("Soopah.XNA.Input.dll");

            //set the default status
            Status = CompilerStatus.Success;

            try
            {
                //compile the source code
                CompilerResults cr = cp.CompileAssemblyFromSource(param, Code);

                //check if the code has warnings
                //if so then elevate status to a warning
                if (cr.Errors.HasWarnings)
                    Status = CompilerStatus.Warning;

                //check if the code has errors
                //if so then elevate status to a error
                if (cr.Errors.HasErrors)
                    Status = CompilerStatus.Error;

                //if the code has any warnings or errors, format them as strings
                if (cr.Errors.Count > 0)
                    Errors = GetErrorStrings(cr.Errors);
                else
                    Errors = null; //else set errors to null

                //make sure the code does not have errors before trying to load it.
                if (!cr.Errors.HasErrors)
                    CodeHost.AssemblyPath = cr.PathToAssembly; //if not then set the user code assembly path.
            }
            catch (Exception ex)
            {
                //catch any unresolvable errors and show them to the user.
                Status = CompilerStatus.AppError;
                Errors = new string[] { ex.ToString() };
            }
        }
 /// <inheritdoc />
 public bool Initialize()
 {
     lock (CompilerLock)
     {
         if (!CompilerIdleNoLock())
         {
             return(false);
         }
         lastCompilerError     = null;
         compilerCurrentStatus = CompilerStatus.Initializing;
         CompilerThread        = new Thread(new ThreadStart(InitializeImpl));
         CompilerThread.Start();
         return(true);
     }
 }
Example #5
0
 //kicks off the compiler thread
 /// <inheritdoc />
 public bool Compile(bool silent = false)
 {
     lock (CompilerLock)
     {
         if (compilerCurrentStatus != CompilerStatus.Initialized)
         {
             return(false);
         }
         silentCompile         = silent;
         lastCompilerError     = null;
         compilerCurrentStatus = CompilerStatus.Compiling;
         CompilerThread        = new Thread(new ThreadStart(CompileImpl));
         CompilerThread.Start();
     }
     return(true);
 }
Example #6
0
        /// <inheritdoc />
        public async Task <Models.CompileJob> Compile(Models.RevisionInformation revisionInformation, DreamMakerSettings dreamMakerSettings, DreamDaemonSecurity securityLevel, uint apiValidateTimeout, IRepository repository, CancellationToken cancellationToken)
        {
            if (revisionInformation == null)
            {
                throw new ArgumentNullException(nameof(revisionInformation));
            }

            if (dreamMakerSettings == null)
            {
                throw new ArgumentNullException(nameof(dreamMakerSettings));
            }

            if (repository == null)
            {
                throw new ArgumentNullException(nameof(repository));
            }

            if (securityLevel == DreamDaemonSecurity.Ultrasafe)
            {
                throw new ArgumentOutOfRangeException(nameof(securityLevel), securityLevel, "Cannot compile with ultrasafe security!");
            }

            logger.LogTrace("Begin Compile");

            var job = new Models.CompileJob
            {
                DirectoryName       = Guid.NewGuid(),
                DmeName             = dreamMakerSettings.ProjectName,
                RevisionInformation = revisionInformation
            };

            logger.LogTrace("Compile output GUID: {0}", job.DirectoryName);

            lock (this)
            {
                if (Status != CompilerStatus.Idle)
                {
                    throw new JobException("There is already a compile in progress!");
                }

                Status = CompilerStatus.Copying;
            }

            try
            {
                var    commitInsert = revisionInformation.CommitSha.Substring(0, 7);
                string remoteCommitInsert;
                if (revisionInformation.CommitSha == revisionInformation.OriginCommitSha)
                {
                    commitInsert       = String.Format(CultureInfo.InvariantCulture, "^{0}", commitInsert);
                    remoteCommitInsert = String.Empty;
                }
                else
                {
                    remoteCommitInsert = String.Format(CultureInfo.InvariantCulture, ". Remote commit: ^{0}", revisionInformation.OriginCommitSha.Substring(0, 7));
                }

                var testmergeInsert = revisionInformation.ActiveTestMerges.Count == 0 ? String.Empty : String.Format(CultureInfo.InvariantCulture, " (Test Merges: {0})",
                                                                                                                     String.Join(", ", revisionInformation.ActiveTestMerges.Select(x => x.TestMerge).Select(x =>
                {
                    var result = String.Format(CultureInfo.InvariantCulture, "#{0} at {1}", x.Number, x.PullRequestRevision.Substring(0, 7));
                    if (x.Comment != null)
                    {
                        result += String.Format(CultureInfo.InvariantCulture, " ({0})", x.Comment);
                    }
                    return(result);
                })));

                using (var byondLock = await byond.UseExecutables(null, cancellationToken).ConfigureAwait(false))
                {
                    await chat.SendUpdateMessage(String.Format(CultureInfo.InvariantCulture, "Deploying revision: {0}{1}{2} BYOND Version: {3}", commitInsert, testmergeInsert, remoteCommitInsert, byondLock.Version), cancellationToken).ConfigureAwait(false);

                    async Task CleanupFailedCompile(bool cancelled)
                    {
                        logger.LogTrace("Cleaning compile directory...");
                        Status = CompilerStatus.Cleanup;
                        var chatTask = chat.SendUpdateMessage(cancelled ? "Deploy cancelled!" : "Deploy failed!", cancellationToken);

                        try
                        {
                            await ioManager.DeleteDirectory(job.DirectoryName.ToString(), CancellationToken.None).ConfigureAwait(false);
                        }
                        catch (Exception e)
                        {
                            logger.LogWarning("Error cleaning up compile directory {0}! Exception: {1}", ioManager.ResolvePath(job.DirectoryName.ToString()), e);
                        }

                        await chatTask.ConfigureAwait(false);
                    };

                    try
                    {
                        await ioManager.CreateDirectory(job.DirectoryName.ToString(), cancellationToken).ConfigureAwait(false);

                        var dirA = ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName);
                        var dirB = ioManager.ConcatPath(job.DirectoryName.ToString(), BDirectoryName);

                        logger.LogTrace("Copying repository to game directory...");
                        //copy the repository
                        var fullDirA   = ioManager.ResolvePath(dirA);
                        var repoOrigin = repository.Origin;
                        using (repository)
                            await repository.CopyTo(fullDirA, cancellationToken).ConfigureAwait(false);

                        Status = CompilerStatus.PreCompile;

                        var resolvedGameDirectory = ioManager.ResolvePath(ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName));
                        await eventConsumer.HandleEvent(EventType.CompileStart, new List <string> {
                            resolvedGameDirectory, repoOrigin
                        }, cancellationToken).ConfigureAwait(false);

                        Status = CompilerStatus.Modifying;

                        if (job.DmeName == null)
                        {
                            logger.LogTrace("Searching for available .dmes...");
                            var path = (await ioManager.GetFilesWithExtension(dirA, DmeExtension, cancellationToken).ConfigureAwait(false)).FirstOrDefault();
                            if (path == default)
                            {
                                throw new JobException("Unable to find any .dme!");
                            }
                            var dmeWithExtension = ioManager.GetFileName(path);
                            job.DmeName = dmeWithExtension.Substring(0, dmeWithExtension.Length - DmeExtension.Length - 1);
                        }

                        logger.LogDebug("Selected {0}.dme for compilation!", job.DmeName);

                        await ModifyDme(job, cancellationToken).ConfigureAwait(false);

                        Status = CompilerStatus.Compiling;

                        //run compiler, verify api
                        job.ByondVersion = byondLock.Version.ToString();

                        var exitCode = await RunDreamMaker(byondLock.DreamMakerPath, job, cancellationToken).ConfigureAwait(false);

                        var apiValidated = false;
                        if (exitCode == 0)
                        {
                            Status = CompilerStatus.Verifying;

                            apiValidated = await VerifyApi(apiValidateTimeout, securityLevel, job, byondLock, dreamMakerSettings.ApiValidationPort.Value, cancellationToken).ConfigureAwait(false);
                        }

                        if (!apiValidated)
                        {
                            //server never validated or compile failed
                            await eventConsumer.HandleEvent(EventType.CompileFailure, new List <string> {
                                resolvedGameDirectory, exitCode == 0 ? "1" : "0"
                            }, cancellationToken).ConfigureAwait(false);

                            throw new JobException(exitCode == 0 ? "Validation of the TGS api failed!" : String.Format(CultureInfo.InvariantCulture, "DM exited with a non-zero code: {0}{1}{2}", exitCode, Environment.NewLine, job.Output));
                        }

                        logger.LogTrace("Running post compile event...");
                        Status = CompilerStatus.PostCompile;
                        await eventConsumer.HandleEvent(EventType.CompileComplete, new List <string> {
                            ioManager.ResolvePath(ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName))
                        }, cancellationToken).ConfigureAwait(false);

                        logger.LogTrace("Duplicating compiled game...");
                        Status = CompilerStatus.Duplicating;

                        //duplicate the dmb et al
                        await ioManager.CopyDirectory(dirA, dirB, null, cancellationToken).ConfigureAwait(false);

                        logger.LogTrace("Applying static game file symlinks...");
                        Status = CompilerStatus.Symlinking;

                        //symlink in the static data
                        var symATask = configuration.SymlinkStaticFilesTo(fullDirA, cancellationToken);
                        var symBTask = configuration.SymlinkStaticFilesTo(ioManager.ResolvePath(dirB), cancellationToken);

                        await Task.WhenAll(symATask, symBTask).ConfigureAwait(false);

                        await chat.SendUpdateMessage("Deployment complete! Changes will be applied on next server reboot.", cancellationToken).ConfigureAwait(false);

                        logger.LogDebug("Compile complete!");
                        return(job);
                    }
                    catch (Exception e)
                    {
                        await CleanupFailedCompile(e is OperationCanceledException).ConfigureAwait(false);

                        throw;
                    }
                }
            }
            catch (OperationCanceledException)
            {
                await eventConsumer.HandleEvent(EventType.CompileCancelled, null, default).ConfigureAwait(false);

                throw;
            }
            finally
            {
                Status = CompilerStatus.Idle;
            }
        }
        //Compiler thread
        void CompileImpl()
        {
            if (Thread.CurrentThread.Name == null)
            {
                Thread.CurrentThread.Name = "Compiler Thread";
            }
            try
            {
                if (!RepoConfigsMatch())
                {
                    lock (CompilerLock)
                    {
                        lastCompilerError     = "Repository TGS3.json does not match cached version! Please update the config appropriately!";
                        compilerCurrentStatus = IsInitialized();
                        return;
                    }
                }
                string resurrectee;
                bool   repobusy_check = false;

                lock (RepoLock)
                {
                    if (RepoBusy)
                    {
                        repobusy_check = true;
                    }
                    else
                    {
                        RepoBusy = true;
                    }
                }

                if (repobusy_check)
                {
                    SendMessage("DM: Copy aborted, repo locked!", MessageType.DeveloperInfo);
                    lock (CompilerLock)
                    {
                        lastCompilerError     = "The repo could not be locked for copying";
                        compilerCurrentStatus = CompilerStatus.Initialized;                           //still fairly valid
                        return;
                    }
                }

                string CurrentSha;
                string dmeName, dmePath;
                try
                {
                    bool silent;
                    lock (CompilerLock)
                    {
                        silent        = silentCompile;
                        silentCompile = false;
                    }

                    if (!silent)
                    {
                        SendMessage("DM: Compiling...", MessageType.DeveloperInfo);
                    }

                    resurrectee = GetStagingDir();                      //non-relative

                    var Config            = GetCachedRepoConfig();
                    var deleteExcludeList = new List <string> {
                        BridgeDLLName
                    };
                    deleteExcludeList.AddRange(Config.StaticDirectoryPaths);
                    deleteExcludeList.AddRange(Config.DLLPaths);
                    Helpers.DeleteDirectory(resurrectee, true, deleteExcludeList);


                    Directory.CreateDirectory(resurrectee + "/.git/logs");

                    foreach (var I in Config.StaticDirectoryPaths)
                    {
                        var the_path = Path.Combine(resurrectee, I);
                        if (!Directory.Exists(the_path))
                        {
                            CreateSymlink(Path.Combine(resurrectee, I), RelativePath(Path.Combine(StaticDirs, I)));
                        }
                    }
                    foreach (var I in Config.DLLPaths)
                    {
                        var the_path = Path.Combine(resurrectee, I);
                        if (!File.Exists(the_path))
                        {
                            CreateSymlink(the_path, RelativePath(Path.Combine(StaticDirs, I)));
                        }
                    }

                    if (!File.Exists(Path.Combine(resurrectee, BridgeDLLName)))
                    {
                        CreateSymlink(Path.Combine(resurrectee, BridgeDLLName), RelativePath(BridgeDLLName));
                    }

                    deleteExcludeList.Add(".git");
                    Helpers.CopyDirectory(RelativePath(RepoPath), resurrectee, deleteExcludeList);
                    CurrentSha = GetShaOrBranchNoLock(out string error, false, false);
                    //just the tip
                    const string GitLogsDir = "/.git/logs";
                    Helpers.CopyDirectory(RelativePath(RepoPath + GitLogsDir), resurrectee + GitLogsDir);
                    dmeName = String.Format(CultureInfo.InvariantCulture, "{0}.dme", ProjectName());
                    dmePath = Path.Combine(resurrectee, dmeName);
                    HandleDMEModifications(resurrectee, dmePath);
                    try
                    {
                        File.Copy(RelativePath(PRJobFile), Path.Combine(resurrectee, PRJobFile));
                    }
                    catch { }
                }
                finally
                {
                    lock (RepoLock)
                        RepoBusy = false;
                }

                var res = CreateBackup();
                if (res != null)
                {
                    lock (CompilerLock)
                    {
                        lastCompilerError     = res;
                        compilerCurrentStatus = CompilerStatus.Initialized;
                        return;
                    }
                }

                if (!File.Exists(dmePath))
                {
                    var errorMsg = String.Format("Could not find {0}!", dmeName);
                    SendMessage("DM: " + errorMsg, MessageType.DeveloperInfo);
                    WriteError(errorMsg, EventID.DMCompileCrash);
                    lock (CompilerLock)
                    {
                        lastCompilerError     = errorMsg;
                        compilerCurrentStatus = CompilerStatus.Initialized;
                        return;
                    }
                }

                if (!PrecompileHook())
                {
                    lastCompilerError     = "The precompile hook failed";
                    compilerCurrentStatus = CompilerStatus.Initialized;                       //still fairly valid
                    WriteWarning("Precompile hook failed!", EventID.DMCompileError);
                    return;
                }

                bool stagedBuild;
                lock (ByondLock)
                {
                    stagedBuild = updateStat == ByondStatus.Staged;
                    if (stagedBuild)
                    {
                        updateStat = ByondStatus.CompilingStaged;
                    }
                    else if (GetVersion(ByondVersion.Installed) == null)
                    {
                        lock (CompilerLock)
                        {
                            lastCompilerError     = "BYOND not installed!";
                            compilerCurrentStatus = CompilerStatus.Initialized;
                            return;
                        }
                    }
                }

                using (var DM = new Process())                  //will kill the process if the thread is terminated
                {
                    DM.StartInfo.FileName  = RelativePath(Path.Combine(stagedBuild ? StagingDirectoryInner : ByondDirectory, "bin/dm.exe"));
                    DM.StartInfo.Arguments = String.Format("-clean {0}", dmePath);
                    DM.StartInfo.RedirectStandardOutput = true;
                    DM.StartInfo.UseShellExecute        = false;
                    var OutputList = new StringBuilder();
                    DM.OutputDataReceived += new DataReceivedEventHandler(
                        delegate(object sender, DataReceivedEventArgs e)
                    {
                        OutputList.Append(Environment.NewLine);
                        OutputList.Append(e.Data);
                    }
                        );
                    try
                    {
                        lock (CompilerLock)
                        {
                            if (compilationCancellationRequestation)
                            {
                                return;
                            }
                            canCancelCompilation = true;
                        }

                        DM.Start();
                        DM.BeginOutputReadLine();
                        while (!DM.HasExited)
                        {
                            DM.WaitForExit(100);
                        }
                        DM.CancelOutputRead();

                        lock (CompilerLock)
                        {
                            canCancelCompilation = false;
                            compilationCancellationRequestation = false;
                        }
                    }
                    catch
                    {
                        if (!DM.HasExited)
                        {
                            DM.Kill();
                            DM.WaitForExit();
                        }
                        throw;
                    }
                    finally
                    {
                        lock (CompilerLock)
                        {
                            canCancelCompilation = false;
                        }
                        if (stagedBuild)
                        {
                            lock (ByondLock)
                                updateStat = ByondStatus.Staged;
                            RequestRestart();
                        }
                    }

                    if (DM.ExitCode == 0)
                    {
                        lock (watchdogLock)
                        {
                            //gotta go fast
                            var online = currentStatus == DreamDaemonStatus.Online;
                            if (online)
                            {
                                Proc.Suspend();
                            }
                            try
                            {
                                var rgdl = RelativePath(GameDirLive);
                                if (Directory.Exists(rgdl))
                                {
                                    //these next two lines should be atomic but this is the best we can do
                                    Directory.Delete(rgdl);
                                }
                                CreateSymlink(rgdl, resurrectee);
                            }
                            finally
                            {
                                if (online && !Proc.HasExited)
                                {
                                    Proc.Resume();
                                }
                            }
                        }
                        var staged = DaemonStatus() != DreamDaemonStatus.Offline;
                        if (!PostcompileHook())
                        {
                            lastCompilerError     = "The postcompile hook failed";
                            compilerCurrentStatus = CompilerStatus.Initialized;                               //still fairly valid
                            WriteWarning("Postcompile hook failed!", EventID.DMCompileError);
                            return;
                        }
                        UpdateLiveSha(CurrentSha);
                        var msg = String.Format("Compile complete!{0}", !staged ? "" : " Server will update on reboot.");
                        WorldAnnounce("Server updated, changes will be applied on reboot...");
                        SendMessage("DM: " + msg, MessageType.DeveloperInfo);
                        WriteInfo(msg, EventID.DMCompileSuccess);
                        lock (CompilerLock)
                        {
                            if (staged)
                            {
                                UpdateStaged = true;
                            }
                            lastCompilerError     = null;
                            compilerCurrentStatus = CompilerStatus.Initialized;                               //still fairly valid
                        }
                    }
                    else
                    {
                        SendMessage("DM: Compile failed!", MessageType.DeveloperInfo);                         //Also happens for warnings
                        WriteWarning("Compile error: " + OutputList.ToString(), EventID.DMCompileError);
                        lock (CompilerLock)
                        {
                            lastCompilerError     = "DM compile failure";
                            compilerCurrentStatus = CompilerStatus.Initialized;
                        }
                    }
                }
            }
            catch (ThreadAbortException)
            {
                return;
            }
            catch (Exception e)
            {
                SendMessage("DM: Compiler thread crashed!", MessageType.DeveloperInfo);
                WriteError("Compile manager errror: " + e.ToString(), EventID.DMCompileCrash);
                lock (CompilerLock)
                {
                    lastCompilerError     = e.ToString();
                    compilerCurrentStatus = CompilerStatus.Initialized;                       //still fairly valid
                }
            }
            finally
            {
                lock (CompilerLock)
                {
                    canCancelCompilation = false;
                    if (compilationCancellationRequestation)
                    {
                        compilerCurrentStatus = CompilerStatus.Initialized;
                        compilationCancellationRequestation = false;
                        SendMessage("DM: Compile cancelled!", MessageType.DeveloperInfo);
                        WriteInfo("Compilation cancelled", EventID.DMCompileCancel);
                    }
                }
            }
        }
        //Initializing thread
        public void InitializeImpl()
        {
            Thread.CurrentThread.Name = "Initialization Thread";
            try
            {
                if (DaemonStatus() != DreamDaemonStatus.Offline)
                {
                    lock (CompilerLock)
                    {
                        lastCompilerError     = "Dream daemon must not be running";
                        compilerCurrentStatus = IsInitialized();
                        return;
                    }
                }

                if (!Exists())                 //repo
                {
                    lock (CompilerLock)
                    {
                        lastCompilerError     = "Repository is not setup!";
                        compilerCurrentStatus = IsInitialized();
                        return;
                    }
                }

                if (!RepoConfigsMatch())
                {
                    lock (CompilerLock)
                    {
                        lastCompilerError     = "Repository TGS3.json does not match cached version! Please update the config appropriately!";
                        compilerCurrentStatus = IsInitialized();
                        return;
                    }
                }
                try
                {
                    SendMessage("DM: Setting up symlinks...", MessageType.DeveloperInfo);
                    CleanGameFolder();
                    Helpers.DeleteDirectory(RelativePath(GameDir));

                    Directory.CreateDirectory(RelativePath(GameDirA));
                    Directory.CreateDirectory(RelativePath(GameDirB));

                    var rep_config = GetCachedRepoConfig();

                    if (rep_config != null)
                    {
                        foreach (var I in rep_config.StaticDirectoryPaths)
                        {
                            CreateSymlink(RelativePath(Path.Combine(GameDirA, I)), RelativePath(Path.Combine(StaticDirs, I)));
                        }
                        foreach (var I in rep_config.DLLPaths)
                        {
                            CreateSymlink(RelativePath(Path.Combine(GameDirA, I)), RelativePath(Path.Combine(StaticDirs, I)));
                        }
                    }

                    var rbdlln = RelativePath(BridgeDLLName);
                    CreateSymlink(RelativePath(Path.Combine(GameDirA, BridgeDLLName)), rbdlln);
                    CreateSymlink(RelativePath(Path.Combine(GameDirB, BridgeDLLName)), rbdlln);

                    CreateSymlink(RelativePath(GameDirLive), RelativePath(GameDirA));

                    lock (CompilerLock)
                    {
                        compilerCurrentStatus = CompilerStatus.Compiling;
                        silentCompile         = true;
                    }
                }
                catch (ThreadAbortException)
                {
                    return;
                }
                catch (Exception e)
                {
                    lock (CompilerLock)
                    {
                        SendMessage("DM: Setup failed!", MessageType.DeveloperInfo);
                        WriteError("Compiler Initialization Error: " + e.ToString(), EventID.DMInitializeCrash);
                        lastCompilerError     = e.ToString();
                        compilerCurrentStatus = CompilerStatus.Uninitialized;
                        return;
                    }
                }
            }
            catch (ThreadAbortException)
            {
                return;
            }
            CompileImpl();
        }