From 6e5f89d778f3593730f7cb4025027156c73d71ca Mon Sep 17 00:00:00 2001 From: toole1 <toole1@6fbd10e7-183d-0410-a318-cb416676e4f2> Date: Fri, 15 Jul 2011 23:16:46 +0000 Subject: [PATCH] jack git-svn-id: https://subversion.cs.illinois.edu/svn/cs225@3366 6fbd10e7-183d-0410-a318-cb416676e4f2 --- blindspot.cpp | 0 monad.cpp | 494 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 494 insertions(+) delete mode 100644 blindspot.cpp create mode 100755 monad.cpp diff --git a/blindspot.cpp b/blindspot.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/monad.cpp b/monad.cpp new file mode 100755 index 0000000..5955c51 --- /dev/null +++ b/monad.cpp @@ -0,0 +1,494 @@ +// monad +// For illinois.edu CS 225 spring 2011 +// By Jack Toole + +#include <iostream> +#include <fstream> +#include <map> +#include <sstream> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <time.h> +#include <unistd.h> +#include <vector> + +#include "util.h" +#include "output.h" + +using namespace std; +using namespace util; +using namespace output; + +namespace monad +{ +void find_base_dir(const char * argv0); +void printHelp(); +void printInfo(); +void processArgs(int argc, char ** argv); +void copyRequiredFiles(); +void getLibs(const vector<string> & libs); +string updateFolder(const string & folder, bool link); +string getFolder(const string & folder, bool link); +void importFiles(const string & preservedFolder, const string & sourceFolder, + const string & destFolder, const vector<string> & files); +void exec_command(const string & command); + +string name; + +string testsFolder; +string sourceFolder; +string gradeFolder; +string tempFolder; + +FileMap config; + +int8_t mp_part; +const int8_t no_mp_part = -1; + +namespace opts { +bool clean = true; +bool update = true; +bool staff = false; +#if OPTIMIZE +bool optimize = true; +#else +bool optimize = false; +#endif +} + +namespace version { +const char * official_name = "CS 225 Monad"; +const char * version_name = "awakening"; +const char * date = "15 July 2011"; +} + +} + +string kyleGetDate() { + string dateAndTime = "Current Date and Time: "; + + time_t currentTime; + struct tm * timeStructure; + time(¤tTime); + timeStructure = localtime(¤tTime); + dateAndTime += asctime(timeStructure); + + return dateAndTime; +} + + + +int main(int argc, char ** argv) +{ + using namespace monad; + + // Find monad/ directory + find_base_dir(argv[0]); + + // Read in local config settings. + // Necessary to do this early for [SVN Root] url + readConfig("./", config); + processArgs(argc, argv); + + cout << version::official_name << endl; + cout << "Testing " << name << "..." << endl << endl; + + cout << "Setting up test environment..." << endl; + + // Read in test-specific config settings + if (mp_part == no_mp_part) + readConfig(testsFolder, config); + else + readConfig(testsFolder, config, to_string((int)mp_part)); + + copyRequiredFiles(); + + // Sleep for a second to avoid clock skew warnings + // This cummulatively adds about 5 minutes to grade each mp, + // but with the benefit of avoiding newsgroup posts + // CHANGED: Judging by previous emails, the time needed for + // this would vary significantly. not sure about solution + // sleep(1); + + output::header("Compiling"); + chdir(gradeFolder.c_str()); + + // #define MP_PART_NUMBER in runtests + ofstream mp_part_file; + mp_part_file.open("_mp_part_number.h"); + if (mp_part == no_mp_part) + mp_part_file << "#define MP_PART_NUMBER NO_MP_PART" << endl; + else + mp_part_file << "#define MP_PART_NUMBER " << (int)mp_part << endl; + mp_part_file.close(); + + // run [Pre-Make Commands] config header + const vector<string> & processing_commands = config["Pre-Make Commands"]; + for (size_t i = 0; i < processing_commands.size(); i++) + exec_command(processing_commands[i]); + + string makestr = "/usr/bin/make --warn-undefined-variables"; + if (opts::optimize) + makestr += " OPTIMIZE=on"; + if (!config["Make Options"].empty()) + makestr += " " + config["Make Options"][0]; +#if DEBUG + cout << makestr <<endl; +#endif + + // Compile with make + system(makestr.c_str()); // yes, system is insecure if the user has control + // over config.ini. But students don't. + + cout << endl << endl; + int score = exec("./runtests"); + if (score < 0) + { + output::header("Running tests"); + cout << "Could not execute test cases" << endl << endl; + score = 0; + + cout << endl; + output::total_score(score); + } + + return score; +} + + +void monad::processArgs(int argc, char ** argv) +{ + // Create OptionsMap for options and vector for positional arguments: + OptionsMap options; + vector<string> args; + + // Add our possible options to our map + options.insert(make_pair("solution", false)); + options.insert(make_pair("newtests", true)); + options.insert(make_pair("clean", opts::clean)); + options.insert(make_pair("update", opts::update)); + options.insert(make_pair("staff", false)); + options.insert(make_pair("optimize", opts::optimize)); + options.insert(make_pair("help", false)); + options.insert(make_pair("h", false)); + options.insert(make_pair("info", false)); + + // Read in options and arguments + char * badopt = processOptions(argc, argv, options, args); + if (badopt != NULL) + exit(-1); + + // Save out options + bool solution = options["solution"]; + bool newtests = options["newtests"]; + opts::clean = options["clean"]; + opts::update = options["update"]; + opts::staff = options["staff"]; + + // Help + if (options["help"] || options["h"] || + (!args.empty() && strcasecmp(args[0].c_str(), "help") == 0)) + { + printHelp(); + exit(0); + } + + // Info + if (options["info"]) + { + printInfo(); + exit(0); + } + + // Check argument list length + if (args.empty() || args.size() > 1) + { + cout << "Usage: " << argv[0] << " mp1" << endl; + cout << "Run \'" << argv[0] << " --help\' for more information" << endl; + exit(0); + } + + // Find mp/lab name and tests folder + vector<string> splitname = tokenize(args[0], '.'); + name = splitname[0]; + if (splitname.size() == 1) + mp_part = no_mp_part; + else + mp_part = atoi(splitname[1].c_str()); + + // Clean + if (name == "clean") + { + system("/bin/rm -rf *_grade/ *_tests/ *_newtests/ *_solution/"); + exit(0); + } + + gradeFolder = "./" + name + "_grade/"; + if (!exists(gradeFolder)) opts::clean = true; + + if (opts::clean) + tempFolder = ""; + else + tempFolder = "./" + name + "_temp/"; + + // Find source folder (i.e. ../mp1) + if (solution) + sourceFolder = updateFolder(name + "_solution/", false); + else + sourceFolder = getFolder(name + '/', false); + + // tests folder + if (newtests) + testsFolder = updateFolder(name + "_newtests/", false); + else + testsFolder = updateFolder(name + "_tests/", false); +} + + +void monad::find_base_dir(const char * argv0) +{ + EXIT_IF_ERROR(argv0 == NULL); + size_t argv0len = strlen(argv0); + char * dir = new char[argv0len + 1]; + strncpy(dir, argv0, argv0len); + dir[argv0len] = '\0'; + + size_t i = argv0len + 1; + do + { + i--; + if (argv0[i] == '/') break; + } while (i != 0); + + // Change directory + if (i != 0) + { + dir[i] = '\0'; + EXIT_IF_ERROR(chdir(dir)); + } + delete [] dir; + + // Ensure the dir is correct + if (!exists("./.monadid")) + { + cerr << "Could not find monad directory. " + "Please run ./monad from the directory it is located in." + << endl; + exit(-1); + } +} + + +void monad::printHelp() +{ + cout << "Usage: monad ASSIGNMENT [solution]" << endl + << "Runs the tests for ASSIGNMENT (mp1, lab01, ...)" << endl + << "\'../ASSIGNMENT/\' must exist" << endl + << "If \'../ASSIGNMENT_tests/\' or any necessary library directories do not exist, these will be downloaded to ./ from SVN" << endl << endl + << "monad settings and individual test settings are stored in config.ini files" << endl + << "The following options are available for monad's ./config.ini:" << endl + << "[SVN Root]" << endl + << "; svn base url here (http://.../cs225/)" << endl + << "[Monad Files]" << endl + << "; Any files to be copied from monad ./ to ./ASSIGNMENT/ testing directory" << endl + << "; By default, runtests.cpp is used to run test cases:" << endl + << "runtests.cpp" << endl << endl + << "The following options are available for individual tests' ../ASSIGNMENT_tests/config.ini:" << endl + << "[Required Files]" << endl + << "; Any files that must be copied from the ../ASSIGNMENT directory" << endl + << "main.cpp" << endl + << "[Testing Files]" << endl + << "; Any files that must be copied from ../ASSIGNMENT_tests" << endl + << "; By default, unit_tests.cpp contains the test cases" << endl + << "unit_tests.cpp" << endl + << "[Libraries]" << endl + << "; Any external library folders to be present in the same directory as the" << endl + << "; testing directory. These should be mirrored in [SVN Root]/_public/" << endl + << "; The 'testutil' library in this directory is also available" << endl + << "EasyBMP" << endl + << endl; + +} + +void monad::printInfo() +{ + cout << version::official_name << endl + << "Version " << version::version_name << endl + << "Released " << version::date << endl + << "Developed by Jack Toole Spring/Fall 2011" << endl + << "Copyright 2011 Jack Toole" << endl + << "Full rights granted to Jack Toole. Rights to use and modify granted to" << endl + << "University of Illinois Urbana-Champaign Computer Science Data Structures" << endl + << "instructors and course staff" << endl; +} + + +void monad::copyRequiredFiles() +{ + // Clear out the temp testing folder + if (opts::clean) + forceRemoveDir(gradeFolder); + else + { + forceRemoveDir(tempFolder); + EXIT_IF_ERROR(rename(gradeFolder.c_str(), tempFolder.c_str())); + } + + exec("/bin/mkdir",gradeFolder.c_str()); + + // Copy and link appropriate files - parsed from config.ini + importFiles(tempFolder, "./", gradeFolder, config["Monad Files"]); + importFiles(tempFolder, testsFolder, gradeFolder, config["Testing Files"]); + importFiles("", sourceFolder, gradeFolder, config["Required Files"]); + importFiles(tempFolder, "", gradeFolder, config["Preserved Files"]); + + forceRemoveDir(tempFolder); + + getLibs(config["Libraries"]); +} + + +void monad::importFiles(const string & preservedFolder, const string & sourceFolder, + const string & destFolder, const vector<string> & files) +{ + // 0 for student errors for missing Required Files dir + int student_error_code = ((preservedFolder == "") ? 0 : -1); + + assertExists(destFolder); + if (preservedFolder != "") assertExists(preservedFolder); + if (sourceFolder != "") assertExists(sourceFolder, student_error_code); + + for (size_t i = 0; i < files.size(); i++) + { + string preservedFile = preservedFolder + files[i]; + string destFile = destFolder + files[i]; + + // Move the file from it's preservation instance + if (preservedFolder != "" && exists(preservedFile) && + (sourceFolder == "" || (permissions(preservedFile) & S_IWUSR) == 0)) + { +//!! cout << "mv " << preservedFile << ' ' << destFile << endl; + EXIT_IF_ERROR(rename(preservedFile.c_str(), destFile.c_str())); + } + else if (sourceFolder != "") + { + // copy the file from it's source + string sourceFile = sourceFolder + files[i]; + assertExists(sourceFile, student_error_code); +//!! cout << "cp " << sourceFile << ' ' << destFile << endl; + EXIT_IF_ERROR(exec("/bin/cp", sourceFile.c_str(), destFile.c_str()) != 0); + } + else continue; + + EXIT_IF_ERROR(chmod(destFile.c_str(), + S_IRUSR | (permissions(destFile) & S_IXUSR)) != 0); + } +} + + +void monad::getLibs(const vector<string> & libs) +{ + for (size_t lib_i = 0; lib_i < libs.size(); lib_i++) + { + string folder = updateFolder(libs[lib_i], false); + protectDir(folder); + chdir(gradeFolder.c_str()); + system(("/bin/ln -s ../"+folder+"* ./").c_str()); + chdir(".."); + } +} + +string monad::updateFolder(const string & folder, bool link) +{ + string get = getFolder(folder, link); + if (opts::update) + exec(-1, "/usr/bin/svn","up", get.c_str()); + return get; +} + +string monad::getFolder(const string & folder, bool link) +{ + // Look in the current folder + string target = "./" + folder; + if (exists(target + "/")) + return target + "/"; + + // Look in the parent folder + string source = "../" + folder; + if (exists(source + "/")) + { + if (!link) return source + "/"; + EXIT_IF_ERROR(symlink(source.c_str(), target.c_str()) != 0); + return target + "/"; + } + + // Maybe it actually *is* the parent folder + string cwd = getcwdstr(); + size_t cwd_i = findNthLast(cwd, '/', 2); + if (cwd_i != string::npos && + cwd.length() - cwd_i > folder.length() && + cwd.compare(cwd_i, folder.length(), folder) == 0) + { + if (!link) return "../"; + EXIT_IF_ERROR(symlink("../", target.c_str()) != 0); + return target + "/"; + } + + // Look two directories up and over - why not? If the parent folder is + // the target source folder for the mp/lab, then the tests or libs + // may be two up and over + source = "../../" + folder; + if (exists(source + "/")) + { + if (!link) return source + "/"; + EXIT_IF_ERROR(symlink(source.c_str(), target.c_str()) != 0); + return target + "/"; + } + + // Check Subversion + + const char * svn_config_name = NULL; + const char * svn_subdir = NULL; + if (!opts::staff) + { + svn_config_name = "SVN Root"; + svn_subdir = "/_public/"; + } + else + { + svn_config_name = "Staff SVN"; + svn_subdir = "/_current/"; + } + if (!config[svn_config_name].empty()) + { + string svndir = config[svn_config_name][0] + svn_subdir + folder; + + int svnstatus = exec("/usr/bin/svn","co",svndir.c_str()); // No redirect, need user to type password + if (svnstatus == 0) return target + "/"; + } + + cerr << "Error: " << folder << " not found." << endl; + exit(-1); + return ""; +} + + +// Execute a monad or command line command +void monad::exec_command(const string & command) +{ + vector<string> args = tokenize(command, ' '); + + // Allow processing of special internals + if (args[0] == "rename_main") + { + EXIT_IF_ERROR(args.size() != 3, "rename_main must take 2 arguments: a file and a new name"); + rename_main(args[1], args[2]); + return; + } + + system(command.c_str()); +} + + + -- GitLab