예제 #1
0
        private async Task UploadCompiledFiles(
            int number,
            CompilationRequest request
            )
        {
            Log.Info("Uploading compiled files...");

            string[] filesToUpload =
            {
                "backend.dll",
                "backend.pdb"
            };

            string ga = request.GameId.Substring(0, 2);

            foreach (string file in filesToUpload)
            {
                Log.Debug($"Uploading '{file}'...");

                await s3Client.PutObjectAsync(
                    new PutObjectRequest {
                    FilePath   = $"compilation/{number}/{file}",
                    BucketName = bucket,
                    Key        = $"games/{ga}/{request.GameId}/backends/" +
                                 $"{request.BackendId}/{file}"
                }
                    );
            }
        }
예제 #2
0
        private async Task UploadCompilerOutput(
            CompilationRequest request,
            string compilerOutput
            )
        {
            Log.Info("Uploading compiler output...");

            string ga = request.GameId.Substring(0, 2);

            byte[] buffer = Encoding.UTF8.GetBytes(compilerOutput);
            var    stream = new MemoryStream(buffer);

            await s3Client.PutObjectAsync(
                new PutObjectRequest {
                InputStream = stream,
                BucketName  = bucket,
                Key         = $"games/{ga}/{request.GameId}/backends/" +
                              $"{request.BackendId}/compiler-output.log"
            }
                );
        }
예제 #3
0
        private async Task <CompilationResponse> CompileBackend(
            CompilationRequest request,
            HttpListenerRequest _
            )
        {
            request.Validate();

            try
            {
                var response = await compiler.CompileBackend(request);

                compilationCrashesInRow = 0;

                return(response);
            }
            catch
            {
                // validation errors don't count as they go around
                compilationCrashesInRow++;

                throw;
            }
        }
예제 #4
0
        public async Task <CompilationResponse> CompileBackend(
            CompilationRequest request
            )
        {
            var sw = new Stopwatch();

            sw.Start();

            int number = counter.GetNext();

            Log.Info($"Starting compilation number {number}, of backend " +
                     $"{request.BackendId} for game {request.GameId}...");

            await frameworkRepository.PrepareFramework(request.FrameworkVersion);

            PrepareCompilationDirectory(number);

            await DownloadBackendFiles(number, request.GameId, request.Files);

            var response = await RunTheCscCompiler(number, request);

            await UploadCompilerOutput(request, response.Output);

            if (response.Success)
            {
                await UploadCompiledFiles(number, request);
            }

            RemoveCompilationDirectory(number);

            sw.Stop();
            double secs = Math.Round(sw.Elapsed.TotalSeconds, 3);

            Log.Info($"Compilation number {number} has finished in {secs} seconds!");

            return(response);
        }
예제 #5
0
        private Task <CompilationResponse> RunTheCscCompiler(
            int number,
            CompilationRequest request
            )
        {
            // === prepare process parameters ===

            string cscArguments = string.Join(
                " ",
                GetCompilerArguments(number, request)
                );

            var proc = new Process {
                EnableRaisingEvents = true,
                StartInfo           =
                {
                    UseShellExecute        = false,
                    FileName               = "csc",
                    Arguments              = cscArguments,
                    RedirectStandardInput  = true,
                    RedirectStandardOutput = true,
                    RedirectStandardError  = true,
                    CreateNoWindow         = true
                }
            };

            // === compiler output logging ===

            const int maxCompilerOutput = 1024 * 1024; // 1 MB
            var       compilerOutput    = new StringBuilder(
                capacity: 1024 * 10,                   // 10 KB
                maxCapacity: maxCompilerOutput
                );

            proc.OutputDataReceived += (s, e) => {
                compilerOutput.AppendLine(e.Data);
            };
            proc.ErrorDataReceived += (s, e) => {
                compilerOutput.AppendLine(e.Data);
            };

            // === result handling ===

            var tcs = new TaskCompletionSource <CompilationResponse>();

            proc.Exited += (sender, args) => {
                bool success = proc.ExitCode == 0;
                var  output  = new CompilationResponse {
                    Success = success,
                    Output  = compilerOutput.ToString(),
                    Message = success ?
                              "Compilation was successful." :
                              "Compilation error."
                };

                proc.Dispose();

                tcs.SetResult(output);
            };

            // === bootstrap ===

            proc.Start();
            proc.BeginOutputReadLine();
            proc.BeginErrorReadLine();

            return(tcs.Task);
        }
예제 #6
0
        private IEnumerable <string> GetCompilerArguments(
            int number,
            CompilationRequest request
            )
        {
            string compilationPath = $"compilation/{number}";

            // === constant flags ===

            yield return("-target:library");

            yield return("-platform:anycpu");

            yield return("-optimize+");

            yield return("-utf8output");

            // output file
            yield return($"-out:{compilationPath}/backend.dll");

            // generate .pdb file
            yield return("-debug+");

            yield return($"-pdb:{compilationPath}/backend.pdb");

            // source code path mapping
            yield return("-fullpaths");

            yield return($"-pathmap:{compilationPath}=[Server]");

            // === user-specified flags ===

            // TODO: .NET version stuff
            // (see the README.md section to learn more)

            yield return("-checked" + (request.Checked ? '+' : '-'));

            yield return("-unsafe" + (request.Unsafe ? '+' : '-'));

            yield return("-langversion:" + request.LangVersion);

            // Possible extensions in the future:
            // - warn as error
            // - disable specific warns
            // - other compiler flags

            // === #define preprocessor symbols ===

            foreach (string symbol in request.DefineSymbols)
            {
                yield return("-define:" + symbol);
            }

            // === references ===

            // Unisave Framework references
            var frameworkRefs = frameworkRepository.GetFrameworkReferences(
                request.FrameworkVersion
                );

            foreach (string r in frameworkRefs)
            {
                yield return(r);
            }

            // .dll libraries within the backend files
            foreach (var file in request.Files)
            {
                if (Path.GetExtension(file.path)?.ToLowerInvariant() == ".dll")
                {
                    yield return($"-reference:{compilationPath}/{file.path}");
                }
            }

            // === source code files ===

            foreach (var file in request.Files)
            {
                if (Path.GetExtension(file.path)?.ToLowerInvariant() == ".cs")
                {
                    yield return($"{compilationPath}/{file.path}");
                }
            }
        }