#! /usr/bin/env python import glob import os import os.path import platform import re import shutil import subprocess import sys BASE_DIRECTORY = "platform" CXXTEST_GENERATED = "cxxtest/tests.cc" quiet = True windows = False def file_title(path): return os.path.splitext(os.path.basename(path))[0] expected_example_outputs = {} def load_expected_outputs(): files = glob.glob("expect/*") for file in files: stream = open(file) try: content = stream.read() finally: stream.close() expected_example_outputs[file_title(file)] = content def run(command, catch_warnings = False): if not quiet: print command try: output = subprocess.check_output(command.split(), stderr = subprocess.STDOUT) if not catch_warnings: return output else: if re.search("warning", output) != None: raise subprocess.CalledProcessError(0, command, output) return output except subprocess.CalledProcessError as e: print e.output print command, "failed" sys.exit(1) class Configuration(object): def __init__(self, subdirectory, command, skip_examples = []): self._subdirectory = subdirectory self._command = command self._skip_examples = skip_examples def elaborate(self, include, output, source): command = self._command if re.match("clang|[gc]\+\+", command) != None: return "%s -I%s -Wall -o %s %s" % (command, include, output, source) elif re.match("vc|cl", command) != None: return "%s /I%s /Fe%s %s" % (command, include, output, source) else: raise Exception("unrecognized compiler in '%s'" % command) def make_directories(self): base = self.directory() directories = \ [base, os.path.join(base, "example"), os.path.join(base, "cxxtest"), os.path.join(base, "link"), os.path.join(base, "performance")] for directory in directories: if not os.path.lexists(directory): os.makedirs(directory) def make_all(self): print self._command self.make_directories() base = self.directory() examples = glob.glob("../example/*.cc") example_directory = os.path.join(base, "example") for file in examples: title = file_title(file) if title in self._skip_examples: continue if title not in expected_example_outputs: print "no expected output for example", title sys.exit(1) binary = os.path.join(example_directory, title) + ".exe" command = self.elaborate("..", binary, file) run(command, True) output = run(binary) output = re.sub("\r\n", "\n", output) if output != expected_example_outputs[title]: print output print "output does not match expected output" sys.exit(1) cxxtest_binary = os.path.join(base, "cxxtest", "tests.exe") command = self.elaborate("..", cxxtest_binary, CXXTEST_GENERATED) run(command, True) run(cxxtest_binary) link_sources = glob.glob("link/*.cc") link_binary = os.path.join(base, "link", "link.exe") command = self.elaborate("..", link_binary, " ".join(link_sources)) run(command, True) performance_sources = glob.glob("performance/*.cc") performance_directory = os.path.join(base, "performance") for file in performance_sources: title = file_title(file) binary = os.path.join(performance_directory, title) + ".exe" command = "time " + self.elaborate("..", binary, file) output = run(command, True) output_file = os.path.join(performance_directory, title) + ".times" stream = open(output_file, "w") try: stream.write(output) finally: stream.close() def directory(self): return os.path.join(BASE_DIRECTORY, self._subdirectory) skip_cxx98 = [ "101-special-values", "102-any-underlying", "103-bitset", "104-quine", "8-constexpr" ] skip_strict = ["4-switch", "102-any-underlying"] def modern_gnu(command): return [ Configuration(command + "-c++11", command + " -std=c++11"), Configuration(command + "-strict-conversion", command + " -std=c++11 " + "-DBETTER_ENUMS_STRICT_CONVERSION", skip_strict), Configuration(command + "-all-constexpr", command + " -std=c++11 " + "-DBETTER_ENUMS_CONSTEXPR_TO_STRING"), Configuration(command + "-c++98", command + " -std=c++98", skip_cxx98) ] # g++46 should be one of these, but g++46 c++11 mode currently not supported due # to the presence of this bug and lack of a workaround in enum.h. # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54086 def older_gnu(command): return [ Configuration(command + "-c++0x", command + " -std=c++0x"), Configuration(command + "-strict-conversion", command + " -std=c++0x " + "-DBETTER_ENUMS_STRICT_CONVERSION", skip_strict), Configuration(command + "-all-constexpr", command + " -std=c++0x " + "-DBETTER_ENUMS_CONSTEXPR_TO_STRING"), Configuration(command + "-c++98", command + " -std=c++98", skip_cxx98) ] def gnu_pre_constexpr(command): return [ Configuration(command + "-c++0x-noconstexpr-war", command + " -std=c++0x " + "-DBETTER_ENUMS_NO_CONSTEXPR", skip_cxx98), Configuration(command + "-strict-conversion", command + " -std=c++0x " + "-DBETTER_ENUMS_NO_CONSTEXPR " + "-DBETTER_ENUMS_STRICT_CONVERSION", skip_cxx98 + skip_strict), Configuration(command + "-c++98", command + " -std=c++98", skip_cxx98) ] def gnu_pre_enum_class(command): return [ Configuration(command + "-c++0x-noconstexpr-war", command + " -std=c++0x " + "-DBETTER_ENUMS_NO_CONSTEXPR", skip_cxx98), Configuration(command + "-c++98", command + " -std=c++98", skip_cxx98) ] def vc(command): return [ Configuration(command, command + " /EHsc", skip_cxx98), Configuration(command + "-strict-conversion", command + " /EHsc /DBETTER_ENUMS_STRICT_CONVERSION", skip_cxx98) ] unix_configurations = \ modern_gnu("clang++36") + \ modern_gnu("clang++35") + \ modern_gnu("clang++34") + \ modern_gnu("clang++33") + \ modern_gnu("g++51") + \ modern_gnu("g++49") + \ modern_gnu("g++48") + \ modern_gnu("g++47") + \ gnu_pre_constexpr("g++46") + \ gnu_pre_constexpr("g++45") + \ gnu_pre_constexpr("g++44") + \ gnu_pre_enum_class("g++43") windows_configurations = vc("vc2015") + vc("vc2013") unix_default = Configuration("default", "c++ --std=c++11") windows_default = Configuration("default", "cl /EHsc") def main(): global quiet global windows load_expected_outputs() cxxtest_headers = " ".join(glob.glob(os.path.join("cxxtest", "*.h"))) run("cxxtestgen --error-printer -o %s %s" % (CXXTEST_GENERATED, cxxtest_headers)) if re.search("Windows|CYGWIN", platform.system()) != None: full = windows_configurations default = windows_default windows = True cygwin_fix_command = \ ("sed 's#\"/home#\"C:/cygwin/home#g' %s > $$$$ ; " + \ "mv $$$$ %s") % (CXXTEST_GENERATED, CXXTEST_GENERATED) os.system(cygwin_fix_command) else: full = unix_configurations default = unix_default if len(sys.argv) > 1 and sys.argv[1] == "--all": configurations = full quiet = True else: configurations = [default] quiet = False for configuration in configurations: configuration.make_all() if __name__ == "__main__": main()