| //------------------------------------------------------------------------------------------------------- |
| // Copyright (C) Microsoft. All rights reserved. |
| // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. |
| //------------------------------------------------------------------------------------------------------- |
| // rlregr.c |
| // |
| // assembly regression worker for rl.c |
| |
| |
| #include "rl.h" |
| |
| |
| #define TMP_PREFIX "di" // 2 characters |
| |
| // Currently not overridable |
| #define REGR_PERL "perl" |
| |
| #define DIR_START_CMD "startdir.cmd" |
| #define DIR_END_CMD "enddir.cmd" |
| |
| // <rl> ... </rl> directives: |
| // |
| // PassFo |
| // Doesn't add -FoNUL. to the command line; allows passing of -Fo |
| // explicitly. |
| // |
| // NoAsm |
| // Disables asm file generation. |
| |
| #define PASS_FO_CMD "PassFo" |
| #define NO_ASM_CMD "NoAsm" |
| |
| // |
| // Global variables set before worker threads start, and only accessed |
| // (not set) by the worker threads. |
| // |
| |
| static BOOL NoMasterCompare; |
| |
| |
| LOCAL int |
| DoCommand( |
| const char *path, // full path of directory to run command in |
| const char *cmdbuf, |
| bool displayError = true |
| ) |
| { |
| int rtncode; |
| |
| rtncode = ExecuteCommand(path, cmdbuf); |
| if (rtncode && displayError) { |
| LogOut("ERROR: Command had a return code of %d:", rtncode); |
| LogOut(" %s", cmdbuf); |
| } |
| return rtncode; |
| } |
| |
| LOCAL void __cdecl |
| RegrCleanUp(void) |
| { |
| } |
| |
| // One-time assembly regression initialization. |
| void |
| RegrInit( |
| void |
| ) |
| { |
| atexit(RegrCleanUp); |
| |
| if (getenv_unsafe("REGR_NOMASTERCMP") != NULL) { |
| if (FBaseline) { |
| Fatal("-baseline conflicts with environment variable REGR_NOMASTERCMP."); |
| } |
| NoMasterCompare = TRUE; |
| } |
| else { |
| NoMasterCompare = FALSE; |
| } |
| } |
| |
| // Called at the start of each directory. |
| BOOL |
| RegrStartDir( |
| char *path |
| ) |
| { |
| char full[MAX_PATH]; |
| |
| // Execute directory setup command script if present. |
| |
| sprintf_s(full, "%s\\%s", path, DIR_START_CMD); |
| if (GetFileAttributes(full) != INVALID_FILE_ATTRIBUTES) { |
| Message(""); // newline |
| Message("Executing %s", DIR_START_CMD); |
| if (DoCommand(path, DIR_START_CMD)) |
| return FALSE; |
| Message(""); // newline |
| } |
| |
| return TRUE; |
| } |
| |
| // Called at the end of each directory. |
| BOOL |
| RegrEndDir( |
| char* path |
| ) |
| { |
| char full[MAX_PATH]; |
| |
| // Execute directory shutdown command script if present. |
| |
| sprintf_s(full, "%s\\%s", path, DIR_END_CMD); |
| if (GetFileAttributes(full) != INVALID_FILE_ATTRIBUTES) { |
| Message(""); // newline |
| Message("Executing %s", DIR_END_CMD); |
| if (DoCommand(path, DIR_END_CMD)) |
| return FALSE; |
| Message(""); |
| } |
| |
| return TRUE; |
| } |
| |
| // Called to perform assembly regressions on one file. |
| // Returns -1 for failure, 1 for diff, 0 for okay. |
| int |
| RegrFile( |
| CDirectory* pDir, |
| Test * pTest, |
| TestVariant * pTestVariant |
| ) |
| { |
| FILE *fp; |
| char *p; |
| const char *opts; |
| int x; |
| int rc; |
| int retval; |
| char full[MAX_PATH]; // temporary place for full paths |
| char basename[MAX_PATH]; // base filename |
| const char *asmdir; // dir of generated asm file |
| char masterasmbuf[MAX_PATH]; // name of master asm file |
| char asmbuf[MAX_PATH]; // name of generated asm file |
| char diffbuf[MAX_PATH]; // name of generated diff file |
| char objbuf[MAX_PATH]; // name of generated obj file |
| char fullmasterasmbuf[MAX_PATH]; // path of master asm file |
| char fullasmbuf[MAX_PATH]; // path of generated asm file |
| char fulldiffbuf[MAX_PATH]; // path of generated diff file |
| char fullobjbuf[MAX_PATH]; // path of generated obj file |
| char standardoptbuf[BUFFER_SIZE]; // standard compilation options (-Fa..) |
| char cmdbuf[BUFFER_SIZE]; // compilation command |
| char compilerbuf[BUFFER_SIZE]; // compiler name buffer |
| char buf[BUFFER_SIZE]; |
| char tmp_file1[BUFFER_SIZE]; |
| char tmp_file2[BUFFER_SIZE]; |
| char tmp_file3[BUFFER_SIZE]; |
| BOOL passFo = FALSE; |
| BOOL noAsm = FALSE; |
| |
| // We don't allow non-target conditions for ASM currently, so we should |
| // never see a conditionNodeList. |
| |
| ASSERTNR(pTest->conditionNodeList == NULL); |
| |
| // Check the directives. |
| |
| if (HasInfo(pTest->defaultTestInfo.data[TIK_RL_DIRECTIVES], XML_DELIM, |
| PASS_FO_CMD)) |
| { |
| passFo = TRUE; |
| } |
| if (HasInfo(pTest->defaultTestInfo.data[TIK_RL_DIRECTIVES], XML_DELIM, |
| NO_ASM_CMD)) |
| { |
| noAsm = TRUE; |
| } |
| |
| // Base file name (name.* -> name) |
| |
| strcpy_s(basename, pTest->name); |
| for(p = basename; *p != '.' && *p != '\0'; p++); |
| *p = '\0'; |
| |
| |
| // Determine the standard options. |
| |
| asmdir = pDir->IsBaseline() ? MASTER_DIR : DIFF_DIR; |
| sprintf_s(standardoptbuf, "-AsmDumpMode:%s\\%s.asm", |
| asmdir, basename); |
| |
| switch (TargetMachine) { |
| case TM_WVM: |
| case TM_WVMX86: |
| case TM_WVM64: |
| strcat_s(standardoptbuf, " /BC "); |
| break; |
| } |
| |
| if (BaseCompiler || DiffCompiler) { |
| char *b2; |
| |
| if (pDir->IsBaseline()) |
| b2 = BaseCompiler; |
| else |
| b2 = DiffCompiler; |
| |
| ASSERTNR(b2); |
| |
| sprintf_s(compilerbuf, " -B2 %s", b2); |
| } |
| else |
| { |
| *compilerbuf = '\0'; |
| } |
| |
| // Try to open the source file. |
| |
| sprintf_s(full, "%s\\%s", pDir->GetDirectoryPath(), pTest->name); |
| fp = fopen_unsafe(full, "r"); |
| if (fp == NULL) { |
| LogError("ERROR: Could not open '%s' with error '%s'", pTest->name, strerror_unsafe(errno)); |
| return -1; |
| } |
| fclose(fp); |
| |
| ASSERTNR(pTestVariant->optFlags == NULL); |
| ASSERTNR(pTestVariant->testInfo.data[TIK_LINK_FLAGS] == NULL); |
| |
| opts = pTestVariant->testInfo.data[TIK_COMPILE_FLAGS]; |
| if (opts == NULL) |
| { |
| opts = ""; |
| } |
| |
| // Compute the object name if passing -Fo through. |
| |
| if (passFo) |
| sprintf_s(objbuf, "%s.obj", basename); |
| else |
| sprintf_s(objbuf, "%s\\%s.obj", asmdir, basename); |
| sprintf_s(fullobjbuf, "%s\\%s", pDir->GetDirectoryPath(), objbuf); |
| |
| // Compute the names for the assembly and diff files. |
| |
| sprintf_s(masterasmbuf, "%s\\%s.asm", MASTER_DIR, basename); |
| sprintf_s(asmbuf, "%s\\%s.asm", asmdir, basename); |
| sprintf_s(diffbuf, "%s\\%s.d", DIFF_DIR, basename); |
| |
| sprintf_s(fullmasterasmbuf, "%s\\%s", pDir->GetDirectoryPath(), masterasmbuf); |
| sprintf_s(fullasmbuf, "%s\\%s", pDir->GetDirectoryPath(), asmbuf); |
| sprintf_s(fulldiffbuf, "%s\\%s", pDir->GetDirectoryPath(), diffbuf); |
| |
| // There could be a diff file from previous regressions. Remove it now. |
| |
| if (!pDir->IsBaseline()) |
| DeleteFileIfFound(fulldiffbuf); |
| |
| // Create the compiler command and do the compile. |
| |
| sprintf_s(cmdbuf, "%s%s %s %s %s %s 2>nul 1>nul", |
| JCBinary, compilerbuf, standardoptbuf, opts, EXTRA_CC_FLAGS, |
| pTest->name); |
| |
| if (FVerbose) { |
| strcpy_s(buf, cmdbuf); |
| } |
| else { |
| // For non-verbose, don't display the standard options |
| sprintf_s(buf, "%s%s %s %s %s", |
| JCBinary, compilerbuf, opts, EXTRA_CC_FLAGS, pTest->name); |
| } |
| Message("%s", buf); |
| |
| if (FTest) |
| { |
| return 0; |
| } |
| |
| // There could be a diff file from previous regressions. Remove it now. |
| |
| if (!pDir->IsBaseline()) |
| { |
| DeleteFileIfFound(fulldiffbuf); |
| } |
| |
| if (DoCommand(pDir->GetDirectoryPath(), cmdbuf)) |
| { |
| DeleteFileIfFound(fullasmbuf); |
| return -1; |
| } |
| |
| // If we are passing -Fo through to the compiler, remove the generated |
| // object file. |
| |
| if (passFo) |
| DeleteFileRetryMsg(fullobjbuf); |
| |
| // See if we generated an assembly file. |
| |
| if (GetFileAttributes(fullasmbuf) == INVALID_FILE_ATTRIBUTES) { |
| LogOut("ERROR: Assembly file %s not generated", asmbuf); |
| return -1; |
| } |
| |
| // Create the assembler command and do the asm if necessary. |
| |
| if (REGR_ASM && !noAsm) { |
| sprintf_s(cmdbuf, "%s %s", REGR_ASM, asmbuf); |
| Message("%s", cmdbuf); |
| // ignore the REGR_ASM return code; we still want to do diffs or |
| // update the master with the generated asm file. |
| if (DoCommand(pDir->GetDirectoryPath(), cmdbuf)) { |
| retval = -1; |
| } |
| else { |
| retval = 0; |
| DeleteFileRetryMsg(fullobjbuf); |
| } |
| } else { |
| retval = 0; |
| } |
| |
| // Exit if not comparing to master or doing baselines. |
| |
| if (NoMasterCompare || pDir->IsBaseline()) |
| return retval; |
| |
| // FYI: Up to this point we haven't used fullmasterasmbuf. |
| |
| ASSERTNR(!strcmp(asmdir, DIFF_DIR)); |
| |
| // If the master doesn't exist, consider this a failure. |
| |
| if (GetFileAttributes(fullmasterasmbuf) == INVALID_FILE_ATTRIBUTES) { |
| LogOut("ERROR: %s doesn't exist", fullmasterasmbuf); |
| return -1; |
| } |
| |
| x = DoCompare(fullasmbuf, fullmasterasmbuf); |
| if (x < 0) |
| return -1; |
| |
| // If the files differ, remove potential bogus differences and try again. |
| |
| rc = retval; |
| if (x) { |
| |
| // Remove the possible bogus diffs. |
| |
| // Generate some temporary file names. |
| // Note: these are full pathnames, not relative pathnames. |
| // NOTE: BE VERY CAREFUL TO CLEAN THESE UP! |
| |
| if (mytmpnam(pDir->GetDirectoryPath(), TMP_PREFIX, tmp_file1) == NULL || |
| mytmpnam(pDir->GetDirectoryPath(), TMP_PREFIX, tmp_file2) == NULL || |
| mytmpnam(pDir->GetDirectoryPath(), TMP_PREFIX, tmp_file3) == NULL) { |
| Fatal("Unable to create temporary files"); |
| } |
| |
| ThreadInfo[ThreadId].AddToTmpFileList(tmp_file1); |
| ThreadInfo[ThreadId].AddToTmpFileList(tmp_file2); |
| ThreadInfo[ThreadId].AddToTmpFileList(tmp_file3); |
| |
| sprintf_s(cmdbuf, "%s %s %s %s", |
| REGR_SHOWD, REGR_PERL, fullasmbuf, tmp_file1); |
| if (DoCommand(pDir->GetDirectoryPath(), cmdbuf)) { |
| rc = -1; |
| } |
| else { |
| sprintf_s(cmdbuf, "%s %s %s %s", |
| REGR_SHOWD, REGR_PERL, fullmasterasmbuf, tmp_file2); |
| if (DoCommand(pDir->GetDirectoryPath(), cmdbuf)) { |
| rc = -1; |
| } |
| else { |
| |
| // Compare again. |
| |
| x = DoCompare(tmp_file1, tmp_file2); |
| if (x < 0) { |
| rc = -1; |
| } |
| else if (x) { |
| |
| // They still differ, make a diff file. |
| |
| sprintf_s(cmdbuf, "%s %s %s > %s", |
| REGR_DIFF, tmp_file1, tmp_file2, tmp_file3); |
| DoCommand(pDir->GetDirectoryPath(), cmdbuf, false); |
| sprintf_s(cmdbuf,"%s -p -e \"s/^[<>-].*\\r?\\n//\" < %s > %s", |
| REGR_PERL, tmp_file3, fulldiffbuf); |
| if (DoCommand(pDir->GetDirectoryPath(), cmdbuf)) { |
| rc = -1; |
| } |
| else { |
| Message("%s and %s differ", asmbuf, masterasmbuf); |
| WriteLog("%s has diffs", pTest->name); |
| rc = 1; |
| } |
| } |
| else { |
| // Move the new asm into the master directory |
| |
| DeleteFileRetryMsg(fullmasterasmbuf); |
| if (!MoveFile(fullasmbuf, fullmasterasmbuf)) { |
| LogOut("ERROR: MoveFile(%s, %s) failed with error %d", |
| fullasmbuf, fullmasterasmbuf, GetLastError()); |
| rc = -1; |
| } |
| } |
| } |
| } |
| |
| ThreadInfo[ThreadId].DeleteTmpFileList(); |
| } |
| else { |
| |
| // They don't differ, so delete the assembly file we just created. |
| |
| DeleteFileRetryMsg(fullasmbuf); |
| } |
| |
| return rc; |
| } |