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(&currentTime);
+	timeStructure = localtime(&currentTime);
+	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