Skip to content
This repository has been archived by the owner on Dec 14, 2024. It is now read-only.

Commit

Permalink
Add support for systems that lack gcc/g++/java
Browse files Browse the repository at this point in the history
  • Loading branch information
zmievsa committed Dec 24, 2020
1 parent d099021 commit 8062f3a
Show file tree
Hide file tree
Showing 10 changed files with 60 additions and 35 deletions.
8 changes: 8 additions & 0 deletions autograder/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ def main(argv=None):
if args.version:
print(__version__)
exit(0)
if sys.platform.startswith("win32"):
print(
"Windows is not supported by autograder. If you do not have Linux,"
"try using it through utilities like Windows Subsystem For Linux."
)
exit(1)
elif sys.platform.startswith("darwin"):
print("OSX is not officially supported. Proceed with caution.")
try:
grader = Grader(
current_dir,
Expand Down
2 changes: 1 addition & 1 deletion autograder/__version__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
__title__ = "assignment-autograder"
__description__ = "Automatic assignment grading for instructor use in programming courses"
__version__ = "2.11.1"
__version__ = "2.12.0"
__author__ = "Stanislav Zmiev"
__author_email__ = "[email protected]"
__license__ = "MIT"
2 changes: 1 addition & 1 deletion autograder/config_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def _figure_out_testcase_types(self) -> Dict[str, Type[TestCase]]:
if testcase_types:
return testcase_types
else:
raise AutograderError(f"Couldn't discover a testcase with correct suffix in {self.testcases_dir}")
raise AutograderError(f"Couldn't discover a testcase with supported suffix in {self.testcases_dir}")


T = TypeVar("T")
Expand Down
4 changes: 3 additions & 1 deletion autograder/grader.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ def _gather_testcases(self) -> List[testcases.TestCase]:
arglist = self.config._generate_arglists(test.name)
shutil.copy(test, self.paths.temp_dir)
tests.append(
testcase_type(
testcase_type( # type: ignore # The typing error here appears due to the limitations of python's typing
self.paths.temp_dir / test.name,
self.config.source_file_name,
self.paths.input_dir,
Expand Down Expand Up @@ -189,6 +189,8 @@ def _get_testcase_output(self, submission: Path, student_dir: Path, logger: Buff
total_testcase_score = 0
testcase_results = []
allowed_tests = [t for t in self.tests if t.source_suffix == submission.suffix]
if not allowed_tests:
print(f"No testcases suitable for the submission {submission.name} found.")
if any(isinstance(t, JavaTestCase) for t in allowed_tests):
JavaTestCase.run_additional_testcase_operations_in_student_dir(student_dir)
for test in allowed_tests:
Expand Down
17 changes: 15 additions & 2 deletions autograder/testcases/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
from typing import Dict, Type
from .abstract_base_class import ArgList, TestCase
from .c import CTestCase
from .cpp import CPPTestCase
from .java import JavaTestCase
from .python import PythonTestCase

ALLOWED_LANGUAGES = {

def _is_installed(language_name: str, testcase: TestCase) -> bool:
""" Useful for logging """
if testcase.is_installed():
return True
else:
print(f"Utilities for running {language_name} are not installed. Disabling it.")
return False


ALLOWED_LANGUAGES: Dict[str, Type[TestCase]] = {
"c": CTestCase,
"java": JavaTestCase,
"python": PythonTestCase,
"c++": CPPTestCase,
}
}

ALLOWED_LANGUAGES = {k: v for k, v in ALLOWED_LANGUAGES.items() if _is_installed(k, v)}
12 changes: 11 additions & 1 deletion autograder/testcases/abstract_base_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import sh
from typing_extensions import Protocol

# TODO: I hate these imports. We should only use relative imports because these imports indicate architectural problems.
# TODO: I hate these imports. We should only use relative imports because direct imports indicate architectural problems.
from autograder.util import format_template, get_stderr

from .util.exit_codes import USED_EXIT_CODES, ExitCodeEventType, ExitCodeHandler
Expand All @@ -24,6 +24,11 @@ def __call__(self, *args: str, **kwargs: Any) -> Optional[sh.RunningCommand]:
raise NotImplementedError()


def Command(command: str, *args: Any, **kwargs: Any) -> Optional[sh.Command]:
""" An API for commands that do not throw errors on creation """
return None if shutil.which(command) is None else sh.Command(command, *args, **kwargs)


class ArgList(enum.Enum):
SUBMISSION_PRECOMPILATION = "SUBMISSION_PRECOMPILATION_ARGS"
TESTCASE_PRECOMPILATION = "TESTCASE_PRECOMPILATION_ARGS"
Expand Down Expand Up @@ -51,6 +56,11 @@ def compile_testcase(self, precompiled_submission: Path) -> ShCommand:
pwd = temp/student_dir
"""

@classmethod
@abstractmethod
def is_installed(cls) -> bool:
""" Returns True software necessary to run the testcase is installed on the system """

def __init__(
self,
path: Path,
Expand Down
14 changes: 6 additions & 8 deletions autograder/testcases/c.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,19 @@

import sh

from .abstract_base_class import ArgList, ShCommand, TestCase


if shutil.which("gcc") is not None:
COMPILER = sh.Command("gcc")
else:
COMPILER = None
from .abstract_base_class import ArgList, Command, ShCommand, TestCase


class CTestCase(TestCase):
source_suffix = ".c"
executable_suffix = ".out"
helper_module_name = "test_helper.c"
SUBMISSION_COMPILATION_ARGS = ("-Dscanf_s=scanf", "-Dmain=__student_main__")
compiler = COMPILER
compiler = Command("gcc")

@classmethod
def is_installed(cls) -> bool:
return cls.compiler is not None

@classmethod
def precompile_submission(cls, submission: Path, student_dir: Path, source_file_name: str, arglist):
Expand Down
8 changes: 2 additions & 6 deletions autograder/testcases/cpp.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
from autograder.testcases.abstract_base_class import Command
import shutil
import sh

from .c import CTestCase

if shutil.which("g++") is not None:
COMPILER = sh.Command("g++")
else:
COMPILER = None


class CPPTestCase(CTestCase):
source_suffix = ".cpp"
compiler = COMPILER
compiler = Command("g++")
20 changes: 7 additions & 13 deletions autograder/testcases/java.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,13 @@

from autograder.util import AutograderError

from .abstract_base_class import ArgList, TestCase, TEST_HELPERS_DIR
from .abstract_base_class import ArgList, Command, TestCase, TEST_HELPERS_DIR

PUBLIC_CLASS_MATCHER = re.compile(r"public(?:\w|\s)+class(?:\w|\s)+({)")
JNA_FILE_NAME = "jna.jar"
PATH_TO_JNA_FILE = TEST_HELPERS_DIR / "extra" / JNA_FILE_NAME


if shutil.which("javac") is not None:
COMPILER = sh.Command("javac")
else:
COMPILER = None
if shutil.which("java") is not None:
VM = sh.Command("java")
else:
VM = None


class JavaTestCase(TestCase):
"""Please, ask students to remove their main as it can theoretically
generate errors (not sure how though).
Expand All @@ -33,8 +23,12 @@ class JavaTestCase(TestCase):
source_suffix = ".java"
executable_suffix = ""
helper_module_name = "TestHelper.java"
compiler = COMPILER
virtual_machine = VM
compiler = Command("javac")
virtual_machine = Command("java")

@classmethod
def is_installed(cls) -> bool:
return cls.compiler is not None and cls.virtual_machine is not None

@classmethod
def precompile_submission(cls, submission: Path, student_dir: Path, source_file_name: str, arglist):
Expand Down
8 changes: 6 additions & 2 deletions autograder/testcases/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import sh

from .abstract_base_class import ArgList, TestCase
from .abstract_base_class import ArgList, Command, TestCase

import sys

Expand All @@ -24,7 +24,11 @@ class PythonTestCase(TestCase):
source_suffix = ".py"
executable_suffix = ".pyc"
helper_module_name = "test_helper.py"
interpreter = sh.Command(PYTHON_EXECUTABLE_NAME)
interpreter = Command(PYTHON_EXECUTABLE_NAME)

@classmethod
def is_installed(cls) -> bool:
return cls.interpreter is not None

@classmethod
def precompile_submission(cls, submission: Path, student_dir: Path, source_file_name, arglist) -> Path:
Expand Down

0 comments on commit 8062f3a

Please sign in to comment.